--- /dev/null
+name: Time-range-based MAC-address filter (mactime)
+maintainer: Dave Barach <dave@barachs.net>
+features:
+ - Static / time-range / data quota based MAC address filter
+description: "Device-input/output arc driver level MAC filter.
+ Checks to see if traffic is allowed to/from the given
+ MAC address, and takes the appropriate action.
+ Intended for the home gateway use-case, where WAN traffic
+ is billed per bit."
+state: production
+properties: [API, CLI, MULTITHREAD]
*/
/** \file
- This file defines vpp mactime control-plane API messages
+ This file defines vpp mactime control-plane API messages
*/
-option version = "1.0.0";
+option version = "1.1.0";
-/** \brief api to enable or disable the time-based src mac filter on
+/** \brief api to enable or disable the time-based src mac filter on
an interface
*/
-autoreply define mactime_enable_disable
+autoreply define mactime_enable_disable
{
u32 client_index; /**< client index, from api_main */
u32 context; /**< application context */
};
/** \brief a time range structure
- * times are in double-precision fp seconds since 1/1/1970,
+ * times are in double-precision fp seconds since 1/1/1970,
* which was a Thursday.
*/
typeonly define time_range
{
f64 start; /**< start of the time range */
f64 end; /**< end of the time range */
-};
+};
/** \brief configure per src-mac time ranges
*
* set count = number of ranges
* set each range start/end in seconds since Sunday began
* As in: start/end >= 0.0 && start/end < 7.0 *86400.0
- *
+ *
* to create a (time-range-based) dynamic drop entry:
- * Same procedure to create a dynamic allow entry,
+ * Same procedure to create a dynamic allow entry,
* set drop=1 instead of allow=1
*
* to delete a per src-mac entry (of any kind)
u8 is_add; /**< add=1, del=0 */
u8 drop; /**< drop flag */
u8 allow; /**< allow flag */
+ u8 no_udp_10001; /**< drop udp to port 10001 */
+ u64 data_quota; /**< max bytes this device */
u8 mac_address[6]; /**< src mac address */
u8 device_name[64]; /**< device name */
u32 count; /**< number of time ranges to follow */
/** time ranges, in seconds since Sunday began */
- vl_api_time_range_t ranges[count];
+ vl_api_time_range_t ranges[count];
};
/*
clib_bihash_kv_8_8_t kv;
int found = 1;
clib_bihash_8_8_t *lut = &mm->lookup_table;
+ u64 data_quota;
int i, rv = 0;
feature_init (mm);
+ data_quota = clib_net_to_host_u64 (mp->data_quota);
+
clib_memset (&kv, 0, sizeof (kv));
memcpy (&kv.key, mp->mac_address, sizeof (mp->mac_address));
if (mp->allow)
dp->flags = MACTIME_DEVICE_FLAG_STATIC_ALLOW;
}
+ if (mp->no_udp_10001)
+ dp->flags |= MACTIME_DEVICE_FLAG_DROP_UDP_10001;
+
+ dp->data_quota = data_quota;
/* Add the hash table entry */
kv.value = dp - mm->devices;
clib_bihash_add_del_8_8 (lut, &kv, 1 /* is_add */ );
}
- else /* add more ranges */
+ else /* add more ranges, flags, etc. */
{
dp = pool_elt_at_index (mm->devices, kv.value);
+
for (i = 0; i < clib_net_to_host_u32 (mp->count); i++)
{
clib_timebase_range_t _r, *r = &_r;
r->end = mp->ranges[i].end;
vec_add1 (dp->ranges, r[0]);
}
+
+ if (vec_len (dp->ranges))
+ {
+ /* Set allow/drop based on msg flags */
+ if (mp->drop)
+ dp->flags = MACTIME_DEVICE_FLAG_DYNAMIC_DROP;
+ if (mp->allow)
+ dp->flags = MACTIME_DEVICE_FLAG_DYNAMIC_ALLOW;
+ }
+ else
+ {
+ /* no ranges, it's a static allow/drop */
+ if (mp->drop)
+ dp->flags = MACTIME_DEVICE_FLAG_STATIC_DROP;
+ if (mp->allow)
+ dp->flags = MACTIME_DEVICE_FLAG_STATIC_ALLOW;
+ }
+ if (mp->no_udp_10001)
+ dp->flags |= MACTIME_DEVICE_FLAG_DROP_UDP_10001;
+
+ dp->data_quota = data_quota;
}
}
else /* delete case */
};
/* *INDENT-ON* */
+u8 *
+format_bytes_with_width (u8 * s, va_list * va)
+{
+ uword nbytes = va_arg (*va, u64);
+ int width = va_arg (*va, int);
+ f64 nbytes_f64;
+ u8 *fmt;
+ char *suffix = "";
+
+ fmt = format (0, "%%%d.3f%%s%c", width, 0);
+
+ if (nbytes > (1024ULL * 1024ULL * 1024ULL))
+ {
+ nbytes_f64 = ((f64) nbytes) / (1024.0 * 1024.0 * 1024.0);
+ suffix = "G";
+ }
+ else if (nbytes > (1024ULL * 1024ULL))
+ {
+ nbytes_f64 = ((f64) nbytes) / (1024.0 * 1024.0);
+ suffix = "M";
+ }
+ else if (nbytes > 1024ULL)
+ {
+ nbytes_f64 = ((f64) nbytes) / (1024.0);
+ suffix = "K";
+ }
+ else
+ nbytes_f64 = (f64) nbytes;
+
+ s = format (s, (char *) fmt, nbytes_f64, suffix);
+ vec_free (fmt);
+ return s;
+}
+
static clib_error_t *
show_mactime_command_fn (vlib_main_t * vm,
unformat_input_t * input, vlib_cli_command_t * cmd)
}));
/* *INDENT-ON* */
- vlib_cli_output (vm, "%-15s %18s %14s %10s %10s %10s",
+ vlib_cli_output (vm, "%-15s %18s %14s %10s %11s %10s",
"Device Name", "Addresses", "Status",
"AllowPkt", "AllowByte", "DropPkt");
vlib_get_combined_counter (&mm->allow_counters, dp - mm->devices,
&allow);
vlib_get_combined_counter (&mm->drop_counters, dp - mm->devices, &drop);
- vlib_cli_output (vm, "%-15s %18s %14s %10lld %10lld %10lld",
+ vlib_cli_output (vm, "%-15s %18s %14s %10lld %U %10lld",
dp->device_name, macstring, status_string,
- allow.packets, allow.bytes, drop.packets);
+ allow.packets, format_bytes_with_width, allow.bytes,
+ 10, drop.packets);
+ if (dp->data_quota > 0)
+ vlib_cli_output (vm, "%-54s %s%U", " ", "Quota ",
+ format_bytes_with_width, dp->data_quota, 10);
/* This is really only good for small N... */
for (j = 0; j < vec_len (mm->arp_cache_copy); j++)
{
{
u8 *device_name;
u8 mac_address[6];
+ u64 data_quota;
u32 flags;
clib_timebase_range_t *ranges;
} mactime_device_t;
#define MACTIME_DEVICE_FLAG_STATIC_ALLOW (1<<1)
#define MACTIME_DEVICE_FLAG_DYNAMIC_DROP (1<<2)
#define MACTIME_DEVICE_FLAG_DYNAMIC_ALLOW (1<<3)
+#define MACTIME_DEVICE_FLAG_DROP_UDP_10001 (1<<4)
typedef struct
{
u8 is_add = 1;
u8 allow = 0;
u8 drop = 0;
+ u8 no_udp_10001 = 0;
+ u64 data_quota = 0;
int ret;
int ii;
allow = 1;
else if (unformat (i, "drop-static"))
drop = 1;
+ else if (unformat (i, "no-udp-10001"))
+ no_udp_10001 = 1;
else if (unformat (i, "mac %U", my_unformat_mac_address, mac_address))
mac_set = 1;
else if (unformat (i, "del"))
is_add = 0;
+ else if (unformat (i, "data-quota %lldM", &data_quota))
+ data_quota <<= 20;
+ else if (unformat (i, "data-quota %lldG", &data_quota))
+ data_quota <<= 30;
else
break;
}
mp->is_add = is_add;
mp->drop = drop;
mp->allow = allow;
+ mp->no_udp_10001 = no_udp_10001;
+ mp->data_quota = clib_host_to_net_u64 (data_quota);
memcpy (mp->mac_address, mac_address, sizeof (mp->mac_address));
memcpy (mp->device_name, device_name, vec_len (device_name));
mp->count = clib_host_to_net_u32 (vec_len (rp));
#include <vnet/pg/pg.h>
#include <vppinfra/error.h>
#include <mactime/mactime.h>
+#include <vnet/ip/ip4.h>
typedef struct
{
vlib_node_registration_t mactime_tx_node;
#define foreach_mactime_error \
-_(DROP, "Dropped packets") \
-_(OK, "Permitted packets")
+_(OK, "Permitted packets") \
+_(STATIC_DROP, "Static drop packets") \
+_(RANGE_DROP, "Range drop packets") \
+_(QUOTA_DROP, "Data quota drop packets") \
+_(DROP_10001, "Dropped UDP DST-port 10001")
typedef enum
{
mactime_device_t *dp;
clib_bihash_kv_8_8_t kv;
clib_bihash_8_8_t *lut = &mm->lookup_table;
- u32 packets_ok = 0, packets_dropped = 0;
+ u32 packets_ok = 0;
f64 now;
u32 thread_index = vm->thread_index;
vnet_main_t *vnm = vnet_get_main ();
dp = pool_elt_at_index (mm->devices, device_index0);
+ /* Known device, check for a traffic quota */
+ if (PREDICT_FALSE (dp->data_quota))
+ {
+ vlib_counter_t device_current_count;
+ vlib_get_combined_counter (&mm->allow_counters,
+ dp - mm->devices,
+ &device_current_count);
+ if (device_current_count.bytes >= dp->data_quota)
+ {
+ next0 = MACTIME_NEXT_DROP;
+ b0->error = node->errors[MACTIME_ERROR_QUOTA_DROP];
+ vlib_increment_combined_counter
+ (&mm->drop_counters, thread_index, dp - mm->devices, 1,
+ len0);
+ goto trace0;
+ }
+ }
+
/* Static drop / allow? */
if (PREDICT_FALSE
(dp->flags &
if (dp->flags & MACTIME_DEVICE_FLAG_STATIC_DROP)
{
next0 = MACTIME_NEXT_DROP;
+ b0->error = node->errors[MACTIME_ERROR_STATIC_DROP];
vlib_increment_combined_counter
(&mm->drop_counters, thread_index, dp - mm->devices, 1,
len0);
- packets_dropped++;
}
else /* note next0 set to allow */
{
- vlib_increment_combined_counter
- (&mm->allow_counters, thread_index, dp - mm->devices, 1,
- len0);
- packets_ok++;
+ /*
+ * Special-case mini-ACL for a certain species of
+ * home security DVR which likes to "call home."
+ */
+ if (PREDICT_FALSE
+ (dp->flags & MACTIME_DEVICE_FLAG_DROP_UDP_10001))
+ {
+ ip4_header_t *ip = (void *) (((u8 *) en0) + 14);
+ udp_header_t *udp = (udp_header_t *) (ip + 1);
+ if (ip->protocol != IP_PROTOCOL_UDP)
+ goto pass;
+ if (clib_net_to_host_u16 (udp->dst_port) == 10001 ||
+ clib_net_to_host_u16 (udp->dst_port) == 9603)
+ {
+ next0 = MACTIME_NEXT_DROP;
+ b0->error = node->errors[MACTIME_ERROR_DROP_10001];
+ }
+ else
+ goto pass;
+ }
+ else
+ {
+ pass:
+ vlib_increment_combined_counter
+ (&mm->allow_counters, thread_index, dp - mm->devices,
+ 1, len0);
+ packets_ok++;
+ }
}
goto trace0;
}
vlib_increment_combined_counter
(&mm->drop_counters, thread_index,
dp - mm->devices, 1, len0);
- packets_dropped++;
next0 = MACTIME_NEXT_DROP;
+ b0->error = node->errors[MACTIME_ERROR_RANGE_DROP];
}
else /* it's an allow range, allow it */
{
if (dp->flags & MACTIME_DEVICE_FLAG_DYNAMIC_ALLOW)
{
next0 = MACTIME_NEXT_DROP;
+ b0->error = node->errors[MACTIME_ERROR_STATIC_DROP];
vlib_increment_combined_counter
(&mm->drop_counters, thread_index, dp - mm->devices, 1, len0);
- packets_dropped++;
}
else
{
vlib_put_next_frame (vm, node, next_index, n_left_to_next);
}
- vlib_node_increment_counter (vm, node->node_index,
- MACTIME_ERROR_DROP, packets_dropped);
vlib_node_increment_counter (vm, node->node_index,
MACTIME_ERROR_OK, packets_ok);
return frame->n_vectors;