From be90e8e408f7ae82c21bf9e7d087bdb112e631b1 Mon Sep 17 00:00:00 2001 From: Justin Pettit Date: Wed, 17 Dec 2008 14:24:22 -0800 Subject: [PATCH] Add support for exporting flow information in NetFlow v5 format. This is implemented by having the datapath send a new meta-Flow End message that contains all the information needed by NetFlow v5 and the OpenFlow Flow Expiration messages. secchan grabs these Flow End messages and generates any requested Flow End and NetFlow messages. The Flow End message is implemented as a Nicira vendor extension, but it is only used internally between the datapath and secchan, so the switch is still fully compatible with OpenFlow v0.8.9. NOTE: This change has not been ported to "switch", which means that it is not able to generate NetFlow messages. "switch" is no longer maintained and will be removed from the repository on January 1, 2009. --- ChangeLog | 8 +- datapath/chain.c | 2 +- datapath/datapath.c | 63 +++- datapath/datapath.h | 8 +- datapath/flow.c | 50 +-- datapath/flow.h | 67 +++- datapath/forward.c | 7 +- datapath/linux-2.4/Modules.mk | 1 + .../compat-2.4/include/linux/jiffies.h | 20 ++ .../linux-2.4/compat-2.4/include/linux/time.h | 15 + datapath/nx_msg.c | 7 + datapath/table-hash.c | 13 +- datapath/table-linear.c | 5 +- datapath/table.h | 3 +- include/openflow/nicira-ext.h | 45 ++- lib/ofp-print.c | 78 +++++ lib/vlog-modules.def | 1 + secchan/automake.mk | 2 + secchan/flow-end.c | 321 ++++++++++++++++++ secchan/flow-end.h | 42 +++ secchan/netflow.h | 94 +++++ secchan/secchan.8.in | 5 + secchan/secchan.c | 13 + secchan/secchan.h | 3 + udatapath/automake.mk | 2 + udatapath/chain.c | 5 +- udatapath/chain.h | 5 +- udatapath/datapath.c | 133 ++++---- udatapath/datapath.h | 53 ++- udatapath/nx_msg.c | 58 ++++ udatapath/nx_msg.h | 43 +++ udatapath/switch-flow.c | 24 +- udatapath/switch-flow.h | 9 +- udatapath/table-hash.c | 14 +- udatapath/table-linear.c | 4 +- udatapath/table.h | 4 +- 36 files changed, 1061 insertions(+), 166 deletions(-) create mode 100644 datapath/linux-2.4/compat-2.4/include/linux/time.h create mode 100644 secchan/flow-end.c create mode 100644 secchan/flow-end.h create mode 100644 secchan/netflow.h create mode 100644 udatapath/nx_msg.c create mode 100644 udatapath/nx_msg.h diff --git a/ChangeLog b/ChangeLog index 2eee0c23..c0c5b162 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ -v0.8.9 - 01 Dec 2008 --------------------- +v0.8.9~2 - ?? Jan 2009 +---------------------- + - Added support to secchan for sending NetFlow v5 events + +v0.8.9~1 - 01 Dec 2008 +---------------------- - Added support for IP netmasks - Added new physical port stats - Added IN_PORT virtual port diff --git a/datapath/chain.c b/datapath/chain.c index c270e295..8c2dddad 100644 --- a/datapath/chain.c +++ b/datapath/chain.c @@ -143,7 +143,7 @@ int chain_delete(struct sw_chain *chain, const struct sw_flow_key *key, might_sleep(); for (i = 0; i < chain->n_tables; i++) { struct sw_table *t = chain->tables[i]; - count += t->delete(t, key, out_port, priority, strict); + count += t->delete(chain->dp, t, key, out_port, priority, strict); } return count; diff --git a/datapath/datapath.c b/datapath/datapath.c index 6b7f6453..c4e6a9f6 100644 --- a/datapath/datapath.c +++ b/datapath/datapath.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -25,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -996,34 +998,55 @@ dp_send_port_status(struct net_bridge_port *p, uint8_t status) return send_openflow_skb(skb, NULL); } +/* Convert jiffies_64 to milliseconds. */ +static u64 inline jiffies_64_to_msecs(const u64 j) +{ +#if HZ <= MSEC_PER_SEC && !(MSEC_PER_SEC % HZ) + return (MSEC_PER_SEC / HZ) * j; +#elif HZ > MSEC_PER_SEC && !(HZ % MSEC_PER_SEC) + return (j + (HZ / MSEC_PER_SEC) - 1)/(HZ / MSEC_PER_SEC); +#else + return (j * MSEC_PER_SEC) / HZ; +#endif +} + int -dp_send_flow_expired(struct datapath *dp, struct sw_flow *flow, - enum ofp_flow_expired_reason reason) +dp_send_flow_end(struct datapath *dp, struct sw_flow *flow, + enum nx_flow_end_reason reason) { struct sk_buff *skb; - struct ofp_flow_expired *ofe; + struct nx_flow_end *nfe; - if (!(dp->flags & OFPC_SEND_FLOW_EXP)) + if (!dp->send_flow_end) return 0; - ofe = alloc_openflow_skb(dp, sizeof *ofe, OFPT_FLOW_EXPIRED, 0, &skb); - if (!ofe) + nfe = alloc_openflow_skb(dp, sizeof *nfe, OFPT_VENDOR, 0, &skb); + if (!nfe) return -ENOMEM; - flow_fill_match(&ofe->match, &flow->key); + nfe->header.vendor = htonl(NX_VENDOR_ID); + nfe->header.subtype = htonl(NXT_FLOW_END); + + flow_fill_match(&nfe->match, &flow->key); - ofe->priority = htons(flow->priority); - ofe->reason = reason; - memset(ofe->pad, 0, sizeof ofe->pad); + nfe->priority = htons(flow->priority); + nfe->reason = reason; - ofe->duration = htonl((jiffies - flow->init_time) / HZ); - memset(ofe->pad2, 0, sizeof ofe->pad2); - ofe->packet_count = cpu_to_be64(flow->packet_count); - ofe->byte_count = cpu_to_be64(flow->byte_count); + nfe->tcp_flags = flow->tcp_flags; + nfe->ip_tos = flow->ip_tos; + + memset(nfe->pad, 0, sizeof nfe->pad); + + nfe->init_time = cpu_to_be64(jiffies_64_to_msecs(flow->created)); + nfe->used_time = cpu_to_be64(jiffies_64_to_msecs(flow->used)); + nfe->end_time = cpu_to_be64(jiffies_64_to_msecs(get_jiffies_64())); + + nfe->packet_count = cpu_to_be64(flow->packet_count); + nfe->byte_count = cpu_to_be64(flow->byte_count); return send_openflow_skb(skb, NULL); } -EXPORT_SYMBOL(dp_send_flow_expired); +EXPORT_SYMBOL(dp_send_flow_end); int dp_send_error_msg(struct datapath *dp, const struct sender *sender, @@ -1331,6 +1354,7 @@ static int flow_stats_dump_callback(struct sw_flow *flow, void *private) struct flow_stats_state *s = private; struct ofp_flow_stats *ofs; int length; + uint64_t duration; length = sizeof *ofs + sf_acts->actions_len; if (length + s->bytes_used > s->bytes_allocated) @@ -1352,7 +1376,14 @@ static int flow_stats_dump_callback(struct sw_flow *flow, void *private) ofs->match.pad = 0; ofs->match.tp_src = flow->key.tp_src; ofs->match.tp_dst = flow->key.tp_dst; - ofs->duration = htonl((jiffies - flow->init_time) / HZ); + + /* The kernel doesn't support 64-bit division, so use the 'do_div' + * macro instead. The first argument is replaced with the quotient, + * while the remainder is the return value. */ + duration = get_jiffies_64() - flow->created; + do_div(duration, HZ); + ofs->duration = htonl(duration); + ofs->priority = htons(flow->priority); ofs->idle_timeout = htons(flow->idle_timeout); ofs->hard_timeout = htons(flow->hard_timeout); diff --git a/datapath/datapath.h b/datapath/datapath.h index 48690227..867e502b 100644 --- a/datapath/datapath.h +++ b/datapath/datapath.h @@ -10,6 +10,7 @@ #include #include #include "openflow/openflow.h" +#include "openflow/nicira-ext.h" #include "flow.h" @@ -57,6 +58,9 @@ struct datapath { uint16_t flags; uint16_t miss_send_len; + /* Flag controlling whether Flow End messages are generated. */ + uint8_t send_flow_end; + /* Switch ports. */ struct net_bridge_port *ports[DP_MAX_PORTS]; struct net_bridge_port *local_port; /* OFPP_LOCAL port. */ @@ -95,8 +99,8 @@ void dp_set_origin(struct datapath *, uint16_t, struct sk_buff *); int dp_send_features_reply(struct datapath *, const struct sender *); int dp_send_config_reply(struct datapath *, const struct sender *); int dp_send_port_status(struct net_bridge_port *p, uint8_t status); -int dp_send_flow_expired(struct datapath *, struct sw_flow *, - enum ofp_flow_expired_reason); +int dp_send_flow_end(struct datapath *, struct sw_flow *, + enum nx_flow_end_reason); int dp_send_error_msg(struct datapath *, const struct sender *, uint16_t, uint16_t, const void *, size_t); int dp_update_port_flags(struct datapath *dp, const struct ofp_port_mod *opm); diff --git a/datapath/flow.c b/datapath/flow.c index a8c3368f..8be44bd1 100644 --- a/datapath/flow.c +++ b/datapath/flow.c @@ -10,19 +10,15 @@ #include #include #include -#include #include #include #include #include -#include -#include -#include #include #include -#include #include "openflow/openflow.h" +#include "openflow/nicira-ext.h" #include "compat.h" struct kmem_cache *flow_cache; @@ -156,12 +152,12 @@ void flow_fill_match(struct ofp_match* to, const struct sw_flow_key* from) int flow_timeout(struct sw_flow *flow) { if (flow->idle_timeout != OFP_FLOW_PERMANENT - && time_after(jiffies, flow->used + flow->idle_timeout * HZ)) - return OFPER_IDLE_TIMEOUT; + && time_after64(get_jiffies_64(), flow->used + flow->idle_timeout * HZ)) + return NXFER_IDLE_TIMEOUT; else if (flow->hard_timeout != OFP_FLOW_PERMANENT - && time_after(jiffies, - flow->init_time + flow->hard_timeout * HZ)) - return OFPER_HARD_TIMEOUT; + && time_after64(get_jiffies_64(), + flow->created + flow->hard_timeout * HZ)) + return NXFER_HARD_TIMEOUT; else return -1; } @@ -330,40 +326,6 @@ static int is_snap(const struct eth_snap_hdr *esh) && !memcmp(esh->oui, "\0\0\0", 3)); } -static int iphdr_ok(struct sk_buff *skb) -{ - int nh_ofs = skb_network_offset(skb); - if (skb->len >= nh_ofs + sizeof(struct iphdr)) { - int ip_len = ip_hdrlen(skb); - return (ip_len >= sizeof(struct iphdr) - && pskb_may_pull(skb, nh_ofs + ip_len)); - } - return 0; -} - -static int tcphdr_ok(struct sk_buff *skb) -{ - int th_ofs = skb_transport_offset(skb); - if (pskb_may_pull(skb, th_ofs + sizeof(struct tcphdr))) { - int tcp_len = tcp_hdrlen(skb); - return (tcp_len >= sizeof(struct tcphdr) - && skb->len >= th_ofs + tcp_len); - } - return 0; -} - -static int udphdr_ok(struct sk_buff *skb) -{ - int th_ofs = skb_transport_offset(skb); - return pskb_may_pull(skb, th_ofs + sizeof(struct udphdr)); -} - -static int icmphdr_ok(struct sk_buff *skb) -{ - int th_ofs = skb_transport_offset(skb); - return pskb_may_pull(skb, th_ofs + sizeof(struct icmphdr)); -} - /* Parses the Ethernet frame in 'skb', which was received on 'in_port', * and initializes 'key' to match. Returns 1 if 'skb' contains an IP * fragment, 0 otherwise. */ diff --git a/datapath/flow.h b/datapath/flow.h index d04d7b93..df7d543e 100644 --- a/datapath/flow.h +++ b/datapath/flow.h @@ -5,10 +5,16 @@ #include #include #include +#include #include #include #include #include +#include +#include +#include +#include +#include #include "openflow/openflow.h" @@ -84,7 +90,7 @@ struct sw_flow { uint16_t priority; /* Only used on entries with wildcards. */ uint16_t idle_timeout; /* Idle time before discarding (seconds). */ uint16_t hard_timeout; /* Hard expiration time (seconds) */ - unsigned long used; /* Last used time (in jiffies). */ + uint64_t used; /* Last used time (in jiffies). */ struct sw_flow_actions *sf_acts; @@ -95,10 +101,13 @@ struct sw_flow { void *private; spinlock_t lock; /* Lock this entry...mostly for stat updates */ - unsigned long init_time; /* When the flow was created (in jiffies). */ + uint64_t created; /* When the flow was created (in jiffies_64). */ uint64_t packet_count; /* Number of packets associated with this entry */ uint64_t byte_count; /* Number of bytes associated with this entry */ + uint8_t tcp_flags; /* Union of seen TCP flags. */ + uint8_t ip_tos; /* IP TOS value. */ + struct rcu_head rcu; }; @@ -120,13 +129,65 @@ int flow_timeout(struct sw_flow *); void print_flow(const struct sw_flow_key *); +static inline int iphdr_ok(struct sk_buff *skb) +{ + int nh_ofs = skb_network_offset(skb); + if (skb->len >= nh_ofs + sizeof(struct iphdr)) { + int ip_len = ip_hdrlen(skb); + return (ip_len >= sizeof(struct iphdr) + && pskb_may_pull(skb, nh_ofs + ip_len)); + } + return 0; +} + +static inline int tcphdr_ok(struct sk_buff *skb) +{ + int th_ofs = skb_transport_offset(skb); + if (pskb_may_pull(skb, th_ofs + sizeof(struct tcphdr))) { + int tcp_len = tcp_hdrlen(skb); + return (tcp_len >= sizeof(struct tcphdr) + && skb->len >= th_ofs + tcp_len); + } + return 0; +} + +static inline int udphdr_ok(struct sk_buff *skb) +{ + int th_ofs = skb_transport_offset(skb); + return pskb_may_pull(skb, th_ofs + sizeof(struct udphdr)); +} + +static inline int icmphdr_ok(struct sk_buff *skb) +{ + int th_ofs = skb_transport_offset(skb); + return pskb_may_pull(skb, th_ofs + sizeof(struct icmphdr)); +} + +#define TCP_FLAGS_OFFSET 13 +#define TCP_FLAG_MASK 0x3f + +static inline struct ofp_tcphdr *ofp_tcp_hdr(const struct sk_buff *skb) +{ + return (struct ofp_tcphdr *)skb_transport_header(skb); +} + static inline void flow_used(struct sw_flow *flow, struct sk_buff *skb) { unsigned long flags; - flow->used = jiffies; + flow->used = get_jiffies_64(); spin_lock_irqsave(&flow->lock, flags); + if (flow->key.dl_type == htons(ETH_P_IP) && iphdr_ok(skb)) { + struct iphdr *nh = ip_hdr(skb); + flow->ip_tos = nh->tos; + + if (flow->key.nw_proto == IPPROTO_TCP && tcphdr_ok(skb)) { + uint8_t *tcp = (uint8_t *)tcp_hdr(skb); + flow->tcp_flags |= *(tcp + TCP_FLAGS_OFFSET) & TCP_FLAG_MASK; + } + } + flow->packet_count++; flow->byte_count += skb->len; spin_unlock_irqrestore(&flow->lock, flags); diff --git a/datapath/forward.c b/datapath/forward.c index d54674ac..8b9b15a6 100644 --- a/datapath/forward.c +++ b/datapath/forward.c @@ -226,10 +226,11 @@ add_flow(struct sw_chain *chain, const struct sender *sender, flow->priority = flow->key.wildcards ? ntohs(ofm->priority) : -1; flow->idle_timeout = ntohs(ofm->idle_timeout); flow->hard_timeout = ntohs(ofm->hard_timeout); - flow->used = jiffies; - flow->init_time = jiffies; + flow->used = flow->created = get_jiffies_64(); flow->byte_count = 0; flow->packet_count = 0; + flow->tcp_flags = 0; + flow->ip_tos = 0; spin_lock_init(&flow->lock); memcpy(flow->sf_acts->actions, ofm->actions, actions_len); @@ -347,7 +348,7 @@ recv_vendor(struct sw_chain *chain, const struct sender *sender, return nx_recv_msg(chain, sender, msg); default: if (net_ratelimit()) - printk("Uknown vendor: %#x\n", ntohl(ovh->vendor)); + printk("unknown vendor: 0x%x\n", ntohl(ovh->vendor)); dp_send_error_msg(chain->dp, sender, OFPET_BAD_REQUEST, OFPBRC_BAD_VENDOR, msg, ntohs(ovh->header.length)); return -EINVAL; diff --git a/datapath/linux-2.4/Modules.mk b/datapath/linux-2.4/Modules.mk index 278f16a3..802facef 100644 --- a/datapath/linux-2.4/Modules.mk +++ b/datapath/linux-2.4/Modules.mk @@ -50,6 +50,7 @@ compat24_headers = \ linux-2.4/compat-2.4/include/linux/spinlock.h \ linux-2.4/compat-2.4/include/linux/string.h \ linux-2.4/compat-2.4/include/linux/tcp.h \ + linux-2.4/compat-2.4/include/linux/time.h \ linux-2.4/compat-2.4/include/linux/timer.h \ linux-2.4/compat-2.4/include/linux/types.h \ linux-2.4/compat-2.4/include/linux/udp.h \ diff --git a/datapath/linux-2.4/compat-2.4/include/linux/jiffies.h b/datapath/linux-2.4/compat-2.4/include/linux/jiffies.h index 718fe91d..ae5e314f 100644 --- a/datapath/linux-2.4/compat-2.4/include/linux/jiffies.h +++ b/datapath/linux-2.4/compat-2.4/include/linux/jiffies.h @@ -7,4 +7,24 @@ extern unsigned long volatile jiffies; +/* 'jiffies_64' are not supported in 2.4 kernels. Here we fake + * compatibility by always just returning the plain 'jiffies' value. + * This means jiffies will wrap every 49 days. */ +#define get_jiffies_64(void) ((u64)jiffies) + +/* Same as above, but does so with platform independent 64bit types. + * These must be used when utilizing jiffies_64 (i.e. return value of + * get_jiffies_64() */ +#define time_after64(a,b) \ + (typecheck(__u64, a) && \ + typecheck(__u64, b) && \ + ((__s64)(b) - (__s64)(a) < 0)) +#define time_before64(a,b) time_after64(b,a) + +#define time_after_eq64(a,b) \ + (typecheck(__u64, a) && \ + typecheck(__u64, b) && \ + ((__s64)(a) - (__s64)(b) >= 0)) +#define time_before_eq64(a,b) time_after_eq64(b,a) + #endif diff --git a/datapath/linux-2.4/compat-2.4/include/linux/time.h b/datapath/linux-2.4/compat-2.4/include/linux/time.h new file mode 100644 index 00000000..9f9ed319 --- /dev/null +++ b/datapath/linux-2.4/compat-2.4/include/linux/time.h @@ -0,0 +1,15 @@ +#ifndef __LINUX_TIME_WRAPPER_H +#define __LINUX_TIME_WRAPPER_H 1 + +#include_next + +/* Parameters used to convert the timespec values: */ +#define MSEC_PER_SEC 1000L +#define USEC_PER_MSEC 1000L +#define NSEC_PER_USEC 1000L +#define NSEC_PER_MSEC 1000000L +#define USEC_PER_SEC 1000000L +#define NSEC_PER_SEC 1000000000L +#define FSEC_PER_SEC 1000000000000000L + +#endif diff --git a/datapath/nx_msg.c b/datapath/nx_msg.c index d8d23bbe..839fac8c 100644 --- a/datapath/nx_msg.c +++ b/datapath/nx_msg.c @@ -17,6 +17,13 @@ nx_recv_msg(struct sw_chain *chain, const struct sender *sender, const struct nicira_header *nh = msg; switch (ntohl(nh->subtype)) { + + case NXT_FLOW_END_CONFIG: { + const struct nx_flow_end_config *nfec = msg; + chain->dp->send_flow_end = nfec->enable; + return 0; + } + #ifdef SUPPORT_SNAT case NXT_ACT_SET_CONFIG: { const struct nx_act_config *nac = msg; diff --git a/datapath/table-hash.c b/datapath/table-hash.c index 21a49f98..a8c965e5 100644 --- a/datapath/table-hash.c +++ b/datapath/table-hash.c @@ -112,7 +112,7 @@ static int do_delete(struct sw_flow **bucket, struct sw_flow *flow) /* Returns number of deleted flows. We can ignore the priority * argument, since all exact-match entries are the same (highest) * priority. */ -static int table_hash_delete(struct sw_table *swt, +static int table_hash_delete(struct datapath *dp, struct sw_table *swt, const struct sw_flow_key *key, uint16_t out_port, uint16_t priority, int strict) { @@ -125,6 +125,7 @@ static int table_hash_delete(struct sw_table *swt, if (flow && flow_keys_equal(&flow->key, key) && flow_has_out_port(flow, out_port)) count = do_delete(bucket, flow); + dp_send_flow_end(dp, flow, NXFER_DELETE); } else { unsigned int i; @@ -134,6 +135,7 @@ static int table_hash_delete(struct sw_table *swt, if (flow && flow_matches_desc(&flow->key, key, strict) && flow_has_out_port(flow, out_port)) count += do_delete(bucket, flow); + dp_send_flow_end(dp, flow, NXFER_DELETE); } } th->n_flows -= count; @@ -154,7 +156,7 @@ static int table_hash_timeout(struct datapath *dp, struct sw_table *swt) int reason = flow_timeout(flow); if (reason >= 0) { count += do_delete(bucket, flow); - dp_send_flow_expired(dp, flow, reason); + dp_send_flow_end(dp, flow, reason); } } } @@ -302,14 +304,15 @@ static int table_hash2_modify(struct sw_table *swt, actions, actions_len)); } -static int table_hash2_delete(struct sw_table *swt, +static int table_hash2_delete(struct datapath *dp, struct sw_table *swt, const struct sw_flow_key *key, uint16_t out_port, uint16_t priority, int strict) { struct sw_table_hash2 *t2 = (struct sw_table_hash2 *) swt; - return (table_hash_delete(t2->subtable[0], key, out_port, priority, strict) - + table_hash_delete(t2->subtable[1], key, out_port, + return (table_hash_delete(dp, t2->subtable[0], key, out_port, + priority, strict) + + table_hash_delete(dp, t2->subtable[1], key, out_port, priority, strict)); } diff --git a/datapath/table-linear.c b/datapath/table-linear.c index af760ac4..3026a68b 100644 --- a/datapath/table-linear.c +++ b/datapath/table-linear.c @@ -98,7 +98,7 @@ static int do_delete(struct sw_table *swt, struct sw_flow *flow) return 1; } -static int table_linear_delete(struct sw_table *swt, +static int table_linear_delete(struct datapath *dp, struct sw_table *swt, const struct sw_flow_key *key, uint16_t out_port, uint16_t priority, int strict) { @@ -111,6 +111,7 @@ static int table_linear_delete(struct sw_table *swt, && flow_has_out_port(flow, out_port) && (!strict || (flow->priority == priority))) count += do_delete(swt, flow); + dp_send_flow_end(dp, flow, NXFER_DELETE); } tl->n_flows -= count; return count; @@ -127,7 +128,7 @@ static int table_linear_timeout(struct datapath *dp, struct sw_table *swt) int reason = flow_timeout(flow); if (reason >= 0) { count += do_delete(swt, flow); - dp_send_flow_expired(dp, flow, reason); + dp_send_flow_end(dp, flow, reason); } } tl->n_flows -= count; diff --git a/datapath/table.h b/datapath/table.h index 27a562cd..8be895e4 100644 --- a/datapath/table.h +++ b/datapath/table.h @@ -69,7 +69,8 @@ struct sw_table { * must have that port as an argument for an output action. If * 'strict' is set, wildcards and priority must match. Returns the * number of flows that were deleted. */ - int (*delete)(struct sw_table *table, const struct sw_flow_key *key, + int (*delete)(struct datapath *dp, struct sw_table *table, + const struct sw_flow_key *key, uint16_t out_port, uint16_t priority, int strict); /* Performs timeout processing on all the flow entries in 'table'. diff --git a/include/openflow/nicira-ext.h b/include/openflow/nicira-ext.h index da71be36..ece73c45 100644 --- a/include/openflow/nicira-ext.h +++ b/include/openflow/nicira-ext.h @@ -40,7 +40,14 @@ enum nicira_type { /* Remote command execution reply, sent when the command's execution * completes. The reply body is struct nx_command_reply. */ - NXT_COMMAND_REPLY + NXT_COMMAND_REPLY, + + /* Configure whether Flow End messages should be sent. */ + NXT_FLOW_END_CONFIG, + + /* Sent by switch when a flow ends. These messages are turned into + * ofp_flow_expired and NetFlow messages in user-space. */ + NXT_FLOW_END }; struct nicira_header { @@ -138,4 +145,40 @@ struct nx_command_reply { }; OFP_ASSERT(sizeof(struct nx_command_reply) == 20); +enum nx_flow_end_reason { + NXFER_IDLE_TIMEOUT, /* Flow idle time exceeded idle_timeout. */ + NXFER_HARD_TIMEOUT, /* Time exceeded hard_timeout. */ + NXFER_DELETE, /* Flow was removed by delete command. */ + NXFER_EJECT /* Flow was ejected. */ +}; + +struct nx_flow_end_config { + struct nicira_header header; + uint8_t enable; /* Set to 1 to enable Flow End message + generation. 0 to disable. */ + uint8_t pad[3]; +}; +OFP_ASSERT(sizeof(struct nx_flow_end_config) == 20); + +struct nx_flow_end { + struct nicira_header header; + struct ofp_match match; /* Description of fields. */ + + uint16_t priority; /* Priority level of flow entry. */ + uint8_t reason; /* One of NXFER_*. */ + + uint8_t tcp_flags; /* Union of seen TCP flags. */ + uint8_t ip_tos; /* IP TOS value. */ + + uint8_t pad[7]; /* Align to 64-bits. */ + + uint64_t init_time; /* Time flow started in milliseconds. */ + uint64_t used_time; /* Time entry was last used in milliseconds. */ + uint64_t end_time; /* Time flow ended in milliseconds. */ + + uint64_t packet_count; + uint64_t byte_count; +}; +OFP_ASSERT(sizeof(struct nx_flow_end) == 104); + #endif /* openflow/nicira-ext.h */ diff --git a/lib/ofp-print.c b/lib/ofp-print.c index 41aed2fd..8dcc3bbc 100644 --- a/lib/ofp-print.c +++ b/lib/ofp-print.c @@ -816,6 +816,65 @@ ofp_print_flow_expired(struct ds *string, const void *oh, size_t len, ntohl(ofe->duration), ntohll(ofe->packet_count), ntohll(ofe->byte_count)); } +/* Pretty-print the NXT_FLOW_EXPIRED packet of 'len' bytes at 'oh' to 'string' + * at the given 'verbosity' level. */ +static void +nx_print_flow_end(struct ds *string, const void *oh, size_t len, + int verbosity) +{ + const struct nx_flow_end *nfe = oh; + + ds_put_cstr(string, "nx_flow_end: "); + + if (len < sizeof(*nfe)) { + ds_put_format(string, " (***length=%zu < min_size=%zu***)\n", + len, sizeof(*nfe)); + return; + } + + ofp_print_match(string, &nfe->match, verbosity); + ds_put_cstr(string, " reason="); + switch (nfe->reason) { + case NXFER_IDLE_TIMEOUT: + ds_put_cstr(string, "idle"); + break; + case NXFER_HARD_TIMEOUT: + ds_put_cstr(string, "hard"); + break; + case NXFER_DELETE: + ds_put_cstr(string, "delete"); + break; + case NXFER_EJECT: + ds_put_cstr(string, "eject"); + break; + default: + ds_put_format(string, "**%"PRIu8"**", nfe->reason); + break; + } + ds_put_format(string, + " pri=%"PRIu16" init=%"PRIu64" used=%"PRIu64" end=%"PRIu64, + nfe->match.wildcards ? ntohs(nfe->priority) : (uint16_t)-1, + ntohll(nfe->init_time), ntohll(nfe->used_time), + ntohll(nfe->end_time)); + ds_put_format(string, + " tflags=0x%x tos=0x%x pkts=%"PRIu64" bytes=%"PRIu64"\n", + nfe->tcp_flags, nfe->ip_tos, ntohll(nfe->packet_count), + ntohll(nfe->byte_count)); +} + +static void +nx_print_msg(struct ds *string, const void *oh, size_t len, int verbosity) +{ + const struct nicira_header *nh = oh; + + switch(ntohl(nh->subtype)) + { + case NXT_FLOW_END: + nx_print_flow_end(string, oh, len, verbosity); + return; + } +} + static void ofp_print_port_mod(struct ds *string, const void *oh, size_t len, @@ -1284,6 +1343,19 @@ ofp_echo(struct ds *string, const void *oh, size_t len, int verbosity) } } +static void +ofp_vendor(struct ds *string, const void *oh, size_t len, int verbosity) +{ + const struct ofp_vendor_header *vh = oh; + + switch(ntohl(vh->vendor)) + { + case NX_VENDOR_ID: + return nx_print_msg(string, oh, len, verbosity); + break; + } +} + struct openflow_packet { uint8_t type; const char *name; @@ -1394,6 +1466,12 @@ static const struct openflow_packet packets[] = { sizeof (struct ofp_header), ofp_echo, }, + { + OFPT_VENDOR, + "vendor", + sizeof (struct ofp_vendor_header), + ofp_vendor, + }, }; /* Composes and returns a string representing the OpenFlow packet of 'len' diff --git a/lib/vlog-modules.def b/lib/vlog-modules.def index 2f6a6766..f736cf91 100644 --- a/lib/vlog-modules.def +++ b/lib/vlog-modules.def @@ -13,6 +13,7 @@ VLOG_MODULE(executer) VLOG_MODULE(fail_open) VLOG_MODULE(fault) VLOG_MODULE(flow) +VLOG_MODULE(flow_end) VLOG_MODULE(in_band) VLOG_MODULE(learning_switch) VLOG_MODULE(mac_learning) diff --git a/secchan/automake.mk b/secchan/automake.mk index d5472f05..588e566d 100644 --- a/secchan/automake.mk +++ b/secchan/automake.mk @@ -8,6 +8,8 @@ secchan_secchan_SOURCES = \ secchan/executer.h \ secchan/fail-open.c \ secchan/fail-open.h \ + secchan/flow-end.c \ + secchan/flow-end.h \ secchan/in-band.c \ secchan/in-band.h \ secchan/port-watcher.c \ diff --git a/secchan/flow-end.c b/secchan/flow-end.c new file mode 100644 index 00000000..56d17765 --- /dev/null +++ b/secchan/flow-end.c @@ -0,0 +1,321 @@ +/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford + * Junior University + * + * We are making the OpenFlow specification and associated documentation + * (Software) available for public use and benefit with the expectation + * that others will use, modify and enhance the Software and contribute + * those enhancements back to the community. However, since we would + * like to make the Software available for broadest use, with as few + * restrictions as possible permission is hereby granted, free of + * charge, to any person obtaining a copy of this Software to deal in + * the Software under the copyrights without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * The name and trademarks of copyright holder(s) may NOT be used in + * advertising or publicity pertaining to the Software or any + * derivatives without specific, written prior permission. + */ + +#include +#include "flow-end.h" +#include +#include +#include +#include +#include +#include +#include +#include "openflow/nicira-ext.h" +#include "openflow/openflow.h" +#include "secchan.h" +#include "ofpbuf.h" +#include "vconn.h" +#include "rconn.h" +#include "socket-util.h" +#include "xtoxll.h" +#include "netflow.h" + +#define THIS_MODULE VLM_flow_end +#include "vlog.h" + +struct flow_end_data { + struct rconn *remote_rconn; + struct rconn *local_rconn; + + bool send_ofp_exp; /* Send OpenFlow 'flow expired' messages? */ + + int netflow_fd; /* Socket for NetFlow collector. */ + uint32_t netflow_cnt; /* Flow sequence number for NetFlow. */ +}; + +static int +udp_open(char *dst) +{ + char *save_ptr; + const char *host_name; + const char *port_string; + struct sockaddr_in sin; + int retval; + int fd; + + /* Glibc 2.7 has a bug in strtok_r when compiling with optimization that + * can cause segfaults here: + * http://sources.redhat.com/bugzilla/show_bug.cgi?id=5614. + * Using "::" instead of the obvious ":" works around it. */ + host_name = strtok_r(dst, "::", &save_ptr); + port_string = strtok_r(NULL, "::", &save_ptr); + if (!host_name) { + ofp_error(0, "%s: bad peer name format", dst); + return -EAFNOSUPPORT; + } + if (!port_string) { + ofp_error(0, "%s: bad port format", dst); + return -EAFNOSUPPORT; + } + + memset(&sin, 0, sizeof sin); + sin.sin_family = AF_INET; + if (lookup_ip(host_name, &sin.sin_addr)) { + return -ENOENT; + } + sin.sin_port = htons(atoi(port_string)); + + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + VLOG_ERR("%s: socket: %s", dst, strerror(errno)); + return -errno; + } + + retval = set_nonblocking(fd); + if (retval) { + close(fd); + return -retval; + } + + retval = connect(fd, (struct sockaddr *) &sin, sizeof sin); + if (retval < 0) { + int error = errno; + VLOG_ERR("%s: connect: %s", dst, strerror(error)); + close(fd); + return -error; + } + + return fd; +} + +static void +send_netflow_msg(const struct nx_flow_end *nfe, struct flow_end_data *fe) +{ + struct netflow_v5_header *nf_hdr; + struct netflow_v5_record *nf_rec; + uint8_t buf[sizeof(*nf_hdr) + sizeof(*nf_rec)]; + uint8_t *p = buf; + struct timeval now; + + /* We only send NetFlow messages for fully specified IP flows; any + * entry with a wildcard is ignored. */ + if ((nfe->match.wildcards != 0) + || (nfe->match.dl_type != htons(ETH_TYPE_IP))) { + return; + } + + memset(&buf, 0, sizeof(buf)); + gettimeofday(&now, NULL); + + nf_hdr = (struct netflow_v5_header *)p; + p += sizeof(*nf_hdr); + nf_rec = (struct netflow_v5_record *)p; + + nf_hdr->version = htons(NETFLOW_V5_VERSION); + nf_hdr->count = htons(1); + nf_hdr->sysuptime = htonl((uint32_t)ntohll(nfe->end_time)); + nf_hdr->unix_secs = htonl(now.tv_sec); + nf_hdr->unix_nsecs = htonl(now.tv_usec * 1000); + nf_hdr->flow_seq = htonl(fe->netflow_cnt); + nf_hdr->engine_type = 0; + nf_hdr->engine_id = 0; + nf_hdr->sampling_interval = htons(0); + + nf_rec->src_addr = nfe->match.nw_src; + nf_rec->dst_addr = nfe->match.nw_dst; + nf_rec->nexthop = htons(0); + nf_rec->input = nfe->match.in_port; + nf_rec->output = htons(0); + nf_rec->packet_count = htonl((uint32_t)ntohll(nfe->packet_count)); + nf_rec->byte_count = htonl((uint32_t)ntohll(nfe->byte_count)); + nf_rec->init_time = htonl((uint32_t)ntohll(nfe->init_time)); + nf_rec->used_time = htonl((uint32_t)ntohll(nfe->used_time)); + + if (nfe->match.nw_proto == IP_TYPE_ICMP) { + /* In NetFlow, the ICMP type and code are concatenated and + * placed in the 'dst_port' field. */ + uint8_t type = (uint8_t)ntohs(nfe->match.tp_src); + uint8_t code = (uint8_t)ntohs(nfe->match.tp_dst); + nf_rec->src_port = htons(0); + nf_rec->dst_port = htons((type << 8) | code); + } else { + nf_rec->src_port = nfe->match.tp_src; + nf_rec->dst_port = nfe->match.tp_dst; + } + + nf_rec->tcp_flags = nfe->tcp_flags; + nf_rec->ip_proto = nfe->match.nw_proto; + nf_rec->ip_tos = nfe->ip_tos; + + nf_rec->src_as = htons(0); + nf_rec->dst_as = htons(0); + nf_rec->src_mask = 0; + nf_rec->dst_mask = 0; + + send(fe->netflow_fd, buf, sizeof(buf), 0); + fe->netflow_cnt++; +} + +static void +send_ofp_expired(const struct nx_flow_end *nfe, const struct flow_end_data *fe) +{ + struct ofp_flow_expired *ofe; + struct ofpbuf *b; + + if ((nfe->reason != NXFER_IDLE_TIMEOUT) + && (nfe->reason != NXFER_HARD_TIMEOUT)) { + return; + } + + ofe = make_openflow(sizeof(*ofe), OFPT_FLOW_EXPIRED, &b); + ofe->match = nfe->match; + ofe->priority = nfe->priority; + if (nfe->reason == NXFER_IDLE_TIMEOUT) { + ofe->reason = OFPER_IDLE_TIMEOUT; + } else { + ofe->reason = OFPER_HARD_TIMEOUT; + } + /* 'duration' is in seconds, but we keeping track of milliseconds. */ + ofe->duration = htonl((ntohll(nfe->end_time)-ntohll(nfe->init_time))/1000); + ofe->packet_count = nfe->packet_count; + ofe->byte_count = nfe->byte_count; + + rconn_send(fe->remote_rconn, b, NULL); +} + +static void +send_nx_flow_end_config(const struct flow_end_data *fe) +{ + struct nx_flow_end_config *nfec; + struct ofpbuf *b; + + nfec = make_openflow(sizeof(*nfec), OFPT_VENDOR, &b); + nfec->header.vendor = htonl(NX_VENDOR_ID); + nfec->header.subtype = htonl(NXT_FLOW_END_CONFIG); + nfec->enable = fe->send_ofp_exp ? 1 : 0; + + rconn_send(fe->local_rconn, b, NULL); +} + +static bool +flow_end_local_packet_cb(struct relay *r, void *flow_end_) +{ + struct flow_end_data *fe = flow_end_; + struct ofpbuf *msg = r->halves[HALF_LOCAL].rxbuf; + struct nicira_header *request = msg->data; + struct nx_flow_end *nfe = msg->data; + + + if (msg->size < sizeof(*nfe)) { + return false; + } + request = msg->data; + if (request->header.type != OFPT_VENDOR + || request->vendor != htonl(NX_VENDOR_ID) + || request->subtype != htonl(NXT_FLOW_END)) { + return false; + } + + if (fe->netflow_fd >= 0) { + send_netflow_msg(nfe, fe); + } + + if (fe->send_ofp_exp) { + send_ofp_expired(nfe, fe); + } + + /* We always consume these Flow End messages. */ + return true; +} + +static bool +flow_end_remote_packet_cb(struct relay *r, void *flow_end_) +{ + struct flow_end_data *fe = flow_end_; + struct ofpbuf *msg = r->halves[HALF_REMOTE].rxbuf; + struct ofp_switch_config *osc = msg->data; + + /* Check for OFPT_SET_CONFIG messages to see if the controller wants + * to receive 'flow expired' messages. If so, we need to intercept + * the datapath's 'flow end' meta-messages and convert. */ + + if ((msg->size < sizeof(*osc)) + || (osc->header.type != OFPT_SET_CONFIG)) { + return false; + } + + if (osc->flags & htons(OFPC_SEND_FLOW_EXP)) { + fe->send_ofp_exp = true; + } else { + fe->send_ofp_exp = false; + } + + send_nx_flow_end_config(fe); + + return false; +} + +static struct hook_class flow_end_hook_class = { + flow_end_local_packet_cb, /* local_packet_cb */ + flow_end_remote_packet_cb, /* remote_packet_cb */ + NULL, /* periodic_cb */ + NULL, /* wait_cb */ + NULL, /* closing_cb */ +}; + +void +flow_end_start(struct secchan *secchan, char *netflow_dst, + struct rconn *local, struct rconn *remote) +{ + struct flow_end_data *fe; + + fe = xcalloc(1, sizeof *fe); + + fe->remote_rconn = remote; + fe->local_rconn = local; + + if (netflow_dst) { + fe->netflow_fd = udp_open(netflow_dst); + if (fe->netflow_fd < 0) { + ofp_fatal(0, "NetFlow setup failed"); + } + fe->send_ofp_exp = true; + } else { + fe->netflow_fd = -1; + fe->send_ofp_exp = false; + } + + add_hook(secchan, &flow_end_hook_class, fe); + + send_nx_flow_end_config(fe); +} diff --git a/secchan/flow-end.h b/secchan/flow-end.h new file mode 100644 index 00000000..3fb1f8f8 --- /dev/null +++ b/secchan/flow-end.h @@ -0,0 +1,42 @@ +/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford + * Junior University + * + * We are making the OpenFlow specification and associated documentation + * (Software) available for public use and benefit with the expectation + * that others will use, modify and enhance the Software and contribute + * those enhancements back to the community. However, since we would + * like to make the Software available for broadest use, with as few + * restrictions as possible permission is hereby granted, free of + * charge, to any person obtaining a copy of this Software to deal in + * the Software under the copyrights without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * The name and trademarks of copyright holder(s) may NOT be used in + * advertising or publicity pertaining to the Software or any + * derivatives without specific, written prior permission. + */ + +#ifndef FLOW_END_H +#define FLOW_END_H 1 + +struct secchan; +struct rconn; + +void flow_end_start(struct secchan *, char *, struct rconn *, struct rconn *); + +#endif /* flow-end.h */ diff --git a/secchan/netflow.h b/secchan/netflow.h new file mode 100644 index 00000000..365625a3 --- /dev/null +++ b/secchan/netflow.h @@ -0,0 +1,94 @@ +/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford + * Junior University + * + * We are making the OpenFlow specification and associated documentation + * (Software) available for public use and benefit with the expectation + * that others will use, modify and enhance the Software and contribute + * those enhancements back to the community. However, since we would + * like to make the Software available for broadest use, with as few + * restrictions as possible permission is hereby granted, free of + * charge, to any person obtaining a copy of this Software to deal in + * the Software under the copyrights without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * The name and trademarks of copyright holder(s) may NOT be used in + * advertising or publicity pertaining to the Software or any + * derivatives without specific, written prior permission. + */ + +#ifndef NETFLOW_H +#define NETFLOW_H 1 + +#include + + +#define NETFLOW_V5_VERSION 5 + +/* Every NetFlow v5 message contains the header that follows. This is + * followed by up to thirty records that describe a terminating flow. + * We only send a single record per NetFlow message. + */ +struct netflow_v5_header { + uint16_t version; /* NetFlow version is 5. */ + uint16_t count; /* Number of records in this message. */ + uint32_t sysuptime; /* System uptime in milliseconds. */ + uint32_t unix_secs; /* Number of seconds since Unix epoch. */ + uint32_t unix_nsecs; /* Number of residual nanoseconds + after epoch seconds. */ + uint32_t flow_seq; /* Number of flows since sending + messages began. */ + uint8_t engine_type; /* Set to zero. */ + uint8_t engine_id; /* Set to zero. */ + uint16_t sampling_interval; /* Set to zero. */ +}; +BUILD_ASSERT_DECL(sizeof(struct netflow_v5_header) == 24); + +/* A NetFlow v5 description of a terminating flow. It is preceded by a + * NetFlow v5 header. + */ +struct netflow_v5_record { + uint32_t src_addr; /* Source IP address. */ + uint32_t dst_addr; /* Destination IP address. */ + uint32_t nexthop; /* IP address of next hop. Set to 0. */ + uint16_t input; /* Input interface index. */ + uint16_t output; /* Output interface index. */ + uint32_t packet_count; /* Number of packets. */ + uint32_t byte_count; /* Number of bytes. */ + uint32_t init_time; /* Value of sysuptime on first packet. */ + uint32_t used_time; /* Value of sysuptime on last packet. */ + + /* The 'src_port' and 'dst_port' identify the source and destination + * port, respectively, for TCP and UDP. For ICMP, the high-order + * byte identifies the type and low-order byte identifies the code + * in the 'dst_port' field. */ + uint16_t src_port; + uint16_t dst_port; + + uint8_t pad1; + uint8_t tcp_flags; /* Union of seen TCP flags. */ + uint8_t ip_proto; /* IP protocol. */ + uint8_t ip_tos; /* IP TOS value. */ + uint16_t src_as; /* Source AS ID. Set to 0. */ + uint16_t dst_as; /* Destination AS ID. Set to 0. */ + uint8_t src_mask; /* Source mask bits. Set to 0. */ + uint8_t dst_mask; /* Destination mask bits. Set to 0. */ + uint8_t pad[2]; +}; +BUILD_ASSERT_DECL(sizeof(struct netflow_v5_record) == 48); + +#endif /* netflow.h */ diff --git a/secchan/secchan.8.in b/secchan/secchan.8.in index 3c5cc62e..79380d02 100644 --- a/secchan/secchan.8.in +++ b/secchan/secchan.8.in @@ -388,6 +388,11 @@ Sets the directory searched for remote command execution to \fBdirectory\fR. The default directory is \fB@pkgdatadir@/commands\fR. +.TP +\fB--netflow=\fIhost\fB:\fIport\fR +When flows end on the switch, send NetFlow v5 messages to +\fIhost\fR on UDP \fIport\fR. + .TP \fB-p\fR, \fB--private-key=\fIprivkey.pem\fR Specifies a PEM file containing the private key used as the switch's diff --git a/secchan/secchan.c b/secchan/secchan.c index 5dae49da..25b9e500 100644 --- a/secchan/secchan.c +++ b/secchan/secchan.c @@ -61,6 +61,7 @@ #ifdef SUPPORT_SNAT #include "snat.h" #endif +#include "flow-end.h" #include "stp-secchan.h" #include "status.h" #include "timeval.h" @@ -196,6 +197,7 @@ main(int argc, char *argv[]) #ifdef SUPPORT_SNAT snat_start(&secchan, pw); #endif + flow_end_start(&secchan, s.netflow_dst, local_rconn, remote_rconn); if (s.enable_stp) { stp_start(&secchan, &s, pw, local_rconn, remote_rconn); } @@ -572,6 +574,7 @@ parse_options(int argc, char *argv[], struct settings *s) OPT_IN_BAND, OPT_COMMAND_ACL, OPT_COMMAND_DIR, + OPT_NETFLOW, VLOG_OPTION_ENUMS }; static struct option long_options[] = { @@ -591,6 +594,7 @@ parse_options(int argc, char *argv[], struct settings *s) {"in-band", no_argument, 0, OPT_IN_BAND}, {"command-acl", required_argument, 0, OPT_COMMAND_ACL}, {"command-dir", required_argument, 0, OPT_COMMAND_DIR}, + {"netflow", required_argument, 0, OPT_NETFLOW}, {"verbose", optional_argument, 0, 'v'}, {"help", no_argument, 0, 'h'}, {"version", no_argument, 0, 'V'}, @@ -620,6 +624,7 @@ parse_options(int argc, char *argv[], struct settings *s) s->in_band = true; s->command_acl = ""; s->command_dir = xasprintf("%s/commands", ofp_pkgdatadir); + s->netflow_dst = NULL; for (;;) { int c; @@ -720,6 +725,13 @@ parse_options(int argc, char *argv[], struct settings *s) s->command_dir = optarg; break; + case OPT_NETFLOW: + if (s->netflow_dst) { + ofp_fatal(0, "--netflow may only be specified once"); + } + s->netflow_dst = optarg; + break; + case 'l': if (s->n_listeners >= MAX_MGMT) { ofp_fatal(0, @@ -838,6 +850,7 @@ usage(void) " --out-of-band controller connection is out-of-band\n" " --stp enable 802.1D Spanning Tree Protocol\n" " --no-stp disable 802.1D Spanning Tree Protocol\n" + " --netflow=HOST:PORT send NetFlow v5 messages when flows end\n" "\nRate-limiting of \"packet-in\" messages to the controller:\n" " --rate-limit[=PACKETS] max rate, in packets/s (default: 1000)\n" " --burst-limit=BURST limit on packet credit for idle time\n" diff --git a/secchan/secchan.h b/secchan/secchan.h index 1b08c697..b5948976 100644 --- a/secchan/secchan.h +++ b/secchan/secchan.h @@ -85,6 +85,9 @@ struct settings { /* Remote command execution. */ char *command_acl; /* Command white/blacklist, as shell globs. */ char *command_dir; /* Directory that contains commands. */ + + /* NetFlow logging. */ + char *netflow_dst; /* Host and port to send NetFlow traffic. */ }; struct half { diff --git a/udatapath/automake.mk b/udatapath/automake.mk index 290742b1..207fa5fd 100644 --- a/udatapath/automake.mk +++ b/udatapath/automake.mk @@ -12,6 +12,8 @@ udatapath_udatapath_SOURCES = \ udatapath/dp_act.h \ udatapath/nx_act.c \ udatapath/nx_act.h \ + udatapath/nx_msg.c \ + udatapath/nx_msg.h \ udatapath/udatapath.c \ udatapath/switch-flow.c \ udatapath/switch-flow.h \ diff --git a/udatapath/chain.c b/udatapath/chain.c index 8f09c00e..2ca289da 100644 --- a/udatapath/chain.c +++ b/udatapath/chain.c @@ -60,12 +60,13 @@ static int add_table(struct sw_chain *chain, struct sw_table *table) /* Creates and returns a new chain. Returns NULL if the chain cannot be * created. */ -struct sw_chain *chain_create(void) +struct sw_chain *chain_create(struct datapath *dp) { struct sw_chain *chain = calloc(1, sizeof *chain); if (chain == NULL) return NULL; + chain->dp = dp; if (add_table(chain, table_hash2_create(0x1EDC6F41, TABLE_HASH_MAX_FLOWS, 0x741B8CD7, TABLE_HASH_MAX_FLOWS)) || add_table(chain, table_linear_create(TABLE_LINEAR_MAX_FLOWS))) { @@ -154,7 +155,7 @@ chain_delete(struct sw_chain *chain, const struct sw_flow_key *key, for (i = 0; i < chain->n_tables; i++) { struct sw_table *t = chain->tables[i]; - count += t->delete(t, key, out_port, priority, strict); + count += t->delete(chain->dp, t, key, out_port, priority, strict); } return count; diff --git a/udatapath/chain.h b/udatapath/chain.h index 8427caa5..639ae95b 100644 --- a/udatapath/chain.h +++ b/udatapath/chain.h @@ -41,6 +41,7 @@ struct sw_flow; struct sw_flow_key; struct ofp_action_header; struct list; +struct datapath; #define TABLE_LINEAR_MAX_FLOWS 100 #define TABLE_HASH_MAX_FLOWS 65536 @@ -52,9 +53,11 @@ struct list; struct sw_chain { int n_tables; struct sw_table *tables[CHAIN_MAX_TABLES]; + + struct datapath *dp; }; -struct sw_chain *chain_create(void); +struct sw_chain *chain_create(struct datapath *); struct sw_flow *chain_lookup(struct sw_chain *, const struct sw_flow_key *); int chain_insert(struct sw_chain *, struct sw_flow *); int chain_modify(struct sw_chain *, const struct sw_flow_key *, diff --git a/udatapath/datapath.c b/udatapath/datapath.c index 852ba508..5c3f1374 100644 --- a/udatapath/datapath.c +++ b/udatapath/datapath.c @@ -41,19 +41,19 @@ #include "chain.h" #include "csum.h" #include "flow.h" -#include "list.h" #include "netdev.h" #include "ofpbuf.h" #include "openflow/openflow.h" +#include "openflow/nicira-ext.h" #include "packets.h" #include "poll-loop.h" #include "rconn.h" #include "stp.h" #include "switch-flow.h" #include "table.h" -#include "timeval.h" #include "vconn.h" #include "xtoxll.h" +#include "nx_msg.h" #include "dp_act.h" #define THIS_MODULE VLM_datapath @@ -82,18 +82,6 @@ extern char serial_num; | (1 << OFPAT_SET_TP_SRC) \ | (1 << OFPAT_SET_TP_DST) ) -struct sw_port { - uint32_t config; /* Some subset of OFPPC_* flags. */ - uint32_t state; /* Some subset of OFPPS_* flags. */ - struct datapath *dp; - struct netdev *netdev; - struct list node; /* Element in datapath.ports. */ - unsigned long long int rx_packets, tx_packets; - unsigned long long int rx_bytes, tx_bytes; - unsigned long long int tx_dropped; - uint16_t port_no; -}; - /* The origin of a received OpenFlow message, to enable sending a reply. */ struct sender { struct remote *remote; /* The device that sent the message. */ @@ -117,34 +105,6 @@ struct remote { void *cb_aux; }; -#define DP_MAX_PORTS 255 -BUILD_ASSERT_DECL(DP_MAX_PORTS <= OFPP_MAX); - -struct datapath { - /* Remote connections. */ - struct list remotes; /* All connections (including controller). */ - - /* Listeners. */ - struct pvconn **listeners; - size_t n_listeners; - - time_t last_timeout; - - /* Unique identifier for this datapath */ - uint64_t id; - - struct sw_chain *chain; /* Forwarding rules. */ - - /* Configuration set from controller. */ - uint16_t flags; - uint16_t miss_send_len; - - /* Switch ports. */ - struct sw_port ports[DP_MAX_PORTS]; - struct sw_port *local_port; /* OFPP_LOCAL port, if any. */ - struct list port_list; /* All ports, including local_port. */ -}; - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(60, 60); static struct remote *remote_create(struct datapath *, struct rconn *); @@ -153,8 +113,6 @@ static void remote_wait(struct remote *); static void remote_destroy(struct remote *); static void update_port_flags(struct datapath *, const struct ofp_port_mod *); -static void send_flow_expired(struct datapath *, struct sw_flow *, - enum ofp_flow_expired_reason); static int update_port_status(struct sw_port *p); static void send_port_status(struct sw_port *p, uint8_t status); static void del_switch_port(struct sw_port *p); @@ -215,7 +173,7 @@ dp_new(struct datapath **dp_, uint64_t dpid) dp->listeners = NULL; dp->n_listeners = 0; dp->id = dpid <= UINT64_C(0xffffffffffff) ? dpid : gen_datapath_id(); - dp->chain = chain_create(); + dp->chain = chain_create(dp); if (!dp->chain) { VLOG_ERR("could not create chain"); free(dp); @@ -348,7 +306,7 @@ dp_run(struct datapath *dp) chain_timeout(dp->chain, &deleted); LIST_FOR_EACH_SAFE (f, n, struct sw_flow, node, &deleted) { - send_flow_expired(dp, f, f->reason); + dp_send_flow_end(dp, f, f->reason); list_remove(&f->node); flow_free(f); } @@ -853,22 +811,40 @@ send_port_status(struct sw_port *p, uint8_t status) } void -send_flow_expired(struct datapath *dp, struct sw_flow *flow, - enum ofp_flow_expired_reason reason) +dp_send_flow_end(struct datapath *dp, struct sw_flow *flow, + enum nx_flow_end_reason reason) { struct ofpbuf *buffer; - struct ofp_flow_expired *ofe; - ofe = make_openflow_xid(sizeof *ofe, OFPT_FLOW_EXPIRED, 0, &buffer); - flow_fill_match(&ofe->match, &flow->key); - - ofe->priority = htons(flow->priority); - ofe->reason = reason; - memset(ofe->pad, 0, sizeof ofe->pad); - - ofe->duration = htonl(time_now() - flow->created); - memset(ofe->pad2, 0, sizeof ofe->pad2); - ofe->packet_count = htonll(flow->packet_count); - ofe->byte_count = htonll(flow->byte_count); + struct nx_flow_end *nfe; + + if (!dp->send_flow_end) { + return; + } + + nfe = make_openflow_xid(sizeof *nfe, OFPT_VENDOR, 0, &buffer); + if (!nfe) { + return; + } + nfe->header.vendor = htonl(NX_VENDOR_ID); + nfe->header.subtype = htonl(NXT_FLOW_END); + + flow_fill_match(&nfe->match, &flow->key); + + nfe->priority = htons(flow->priority); + nfe->reason = reason; + + nfe->tcp_flags = flow->tcp_flags; + nfe->ip_tos = flow->ip_tos; + + memset(nfe->pad, 0, sizeof nfe->pad); + + nfe->init_time = htonll(flow->created); + nfe->used_time = htonll(flow->used); + nfe->end_time = htonll(time_msec()); + + nfe->packet_count = htonll(flow->packet_count); + nfe->byte_count = htonll(flow->byte_count); + send_openflow_buffer(dp, buffer, NULL); } @@ -887,7 +863,7 @@ dp_send_error_msg(struct datapath *dp, const struct sender *sender, static void fill_flow_stats(struct ofpbuf *buffer, struct sw_flow *flow, - int table_idx, time_t now) + int table_idx, uint64_t now) { struct ofp_flow_stats *ofs; int length = sizeof *ofs + flow->sf_acts->actions_len; @@ -907,7 +883,7 @@ fill_flow_stats(struct ofpbuf *buffer, struct sw_flow *flow, ofs->match.pad = 0; ofs->match.tp_src = flow->key.flow.tp_src; ofs->match.tp_dst = flow->key.flow.tp_dst; - ofs->duration = htonl(now - flow->created); + ofs->duration = htonl((now - flow->created) / 1000); ofs->priority = htons(flow->priority); ofs->idle_timeout = htons(flow->idle_timeout); ofs->hard_timeout = htons(flow->hard_timeout); @@ -1089,10 +1065,12 @@ add_flow(struct datapath *dp, const struct sender *sender, flow->priority = flow->key.wildcards ? ntohs(ofm->priority) : -1; flow->idle_timeout = ntohs(ofm->idle_timeout); flow->hard_timeout = ntohs(ofm->hard_timeout); - flow->used = flow->created = time_now(); + flow->used = flow->created = time_msec(); flow->sf_acts->actions_len = actions_len; flow->byte_count = 0; flow->packet_count = 0; + flow->tcp_flags = 0; + flow->ip_tos = 0; memcpy(flow->sf_acts->actions, ofm->actions, actions_len); /* Act. */ @@ -1110,8 +1088,8 @@ add_flow(struct datapath *dp, const struct sender *sender, if (buffer) { struct sw_flow_key key; uint16_t in_port = ntohs(ofm->match.in_port); - flow_used(flow, buffer); flow_extract(buffer, in_port, &key.flow); + flow_used(flow, buffer); execute_actions(dp, buffer, &key, ofm->actions, actions_len, false); } else { @@ -1218,7 +1196,7 @@ struct flow_stats_state { int table_idx; struct sw_table_position position; struct ofp_flow_stats_request rq; - time_t now; + uint64_t now; /* Current time in milliseconds */ struct ofpbuf *buffer; }; @@ -1252,7 +1230,7 @@ static int flow_stats_dump(struct datapath *dp, void *state, flow_extract_match(&match_key, &s->rq.match); s->buffer = buffer; - s->now = time_now(); + s->now = time_msec(); while (s->table_idx < dp->chain->n_tables && (s->rq.table_id == 0xff || s->rq.table_id == s->table_idx)) { @@ -1614,6 +1592,25 @@ recv_echo_reply(struct datapath *dp UNUSED, const struct sender *sender UNUSED, return 0; } +static int +recv_vendor(struct datapath *dp, const struct sender *sender, + const void *oh) +{ + const struct ofp_vendor_header *ovh = oh; + + switch (ntohl(ovh->vendor)) + { + case NX_VENDOR_ID: + return nx_recv_msg(dp, sender, oh); + + default: + VLOG_WARN_RL(&rl, "unknown vendor: 0x%x\n", ntohl(ovh->vendor)); + dp_send_error_msg(dp, sender, OFPET_BAD_REQUEST, + OFPBRC_BAD_VENDOR, oh, ntohs(ovh->header.length)); + return -EINVAL; + } +} + /* 'msg', which is 'length' bytes long, was received from the control path. * Apply it to 'chain'. */ int @@ -1669,6 +1666,10 @@ fwd_control_input(struct datapath *dp, const struct sender *sender, min_size = sizeof(struct ofp_header); handler = recv_echo_reply; break; + case OFPT_VENDOR: + min_size = sizeof(struct ofp_vendor_header); + handler = recv_vendor; + break; default: dp_send_error_msg(dp, sender, OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE, msg, length); diff --git a/udatapath/datapath.h b/udatapath/datapath.h index a8cf27f2..37cbdbed 100644 --- a/udatapath/datapath.h +++ b/udatapath/datapath.h @@ -38,11 +38,58 @@ #include #include +#include "openflow/nicira-ext.h" #include "ofpbuf.h" +#include "timeval.h" +#include "list.h" -struct datapath; struct rconn; struct pvconn; +struct sw_flow; +struct sender; + +struct sw_port { + uint32_t config; /* Some subset of OFPPC_* flags. */ + uint32_t state; /* Some subset of OFPPS_* flags. */ + struct datapath *dp; + struct netdev *netdev; + struct list node; /* Element in datapath.ports. */ + unsigned long long int rx_packets, tx_packets; + unsigned long long int rx_bytes, tx_bytes; + unsigned long long int tx_dropped; + uint16_t port_no; +}; + +#define DP_MAX_PORTS 255 +BUILD_ASSERT_DECL(DP_MAX_PORTS <= OFPP_MAX); + +struct datapath { + /* Remote connections. */ + struct list remotes; /* All connections (including controller). */ + + /* Listeners. */ + struct pvconn **listeners; + size_t n_listeners; + + time_t last_timeout; + + /* Unique identifier for this datapath */ + uint64_t id; + + struct sw_chain *chain; /* Forwarding rules. */ + + /* Configuration set from controller. */ + uint16_t flags; + uint16_t miss_send_len; + + /* Flag controlling whether Flow End messages are generated. */ + uint8_t send_flow_end; + + /* Switch ports. */ + struct sw_port ports[DP_MAX_PORTS]; + struct sw_port *local_port; /* OFPP_LOCAL port, if any. */ + struct list port_list; /* All ports, including local_port. */ +}; int dp_new(struct datapath **, uint64_t dpid); int dp_add_port(struct datapath *, const char *netdev); @@ -50,6 +97,10 @@ int dp_add_local_port(struct datapath *, const char *netdev); void dp_add_pvconn(struct datapath *, struct pvconn *); void dp_run(struct datapath *); void dp_wait(struct datapath *); +void dp_send_error_msg(struct datapath *, const struct sender *, + uint16_t, uint16_t, const void *, size_t); +void dp_send_flow_end(struct datapath *, struct sw_flow *, + enum nx_flow_end_reason); void dp_output_port(struct datapath *, struct ofpbuf *, int in_port, int out_port, bool ignore_no_fwd); void dp_output_control(struct datapath *, struct ofpbuf *, int in_port, diff --git a/udatapath/nx_msg.c b/udatapath/nx_msg.c new file mode 100644 index 00000000..0d11e026 --- /dev/null +++ b/udatapath/nx_msg.c @@ -0,0 +1,58 @@ +/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford + * Junior University + * + * We are making the OpenFlow specification and associated documentation + * (Software) available for public use and benefit with the expectation + * that others will use, modify and enhance the Software and contribute + * those enhancements back to the community. However, since we would + * like to make the Software available for broadest use, with as few + * restrictions as possible permission is hereby granted, free of + * charge, to any person obtaining a copy of this Software to deal in + * the Software under the copyrights without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * The name and trademarks of copyright holder(s) may NOT be used in + * advertising or publicity pertaining to the Software or any + * derivatives without specific, written prior permission. + */ + +#include +#include +#include "openflow/nicira-ext.h" +#include "nx_msg.h" + +int nx_recv_msg(struct datapath *dp, const struct sender *sender, + const void *oh) +{ + const struct nicira_header *nh = oh; + + switch (ntohl(nh->subtype)) { + case NXT_FLOW_END_CONFIG: { + const struct nx_flow_end_config *nfec = oh; + dp->send_flow_end = nfec->enable; + return 0; + } + + default: + dp_send_error_msg(dp, sender, OFPET_BAD_REQUEST, + OFPBRC_BAD_SUBTYPE, oh, ntohs(nh->header.length)); + return -EINVAL; + } + + return -EINVAL; +} diff --git a/udatapath/nx_msg.h b/udatapath/nx_msg.h new file mode 100644 index 00000000..4ed272a0 --- /dev/null +++ b/udatapath/nx_msg.h @@ -0,0 +1,43 @@ +/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford + * Junior University + * + * We are making the OpenFlow specification and associated documentation + * (Software) available for public use and benefit with the expectation + * that others will use, modify and enhance the Software and contribute + * those enhancements back to the community. However, since we would + * like to make the Software available for broadest use, with as few + * restrictions as possible permission is hereby granted, free of + * charge, to any person obtaining a copy of this Software to deal in + * the Software under the copyrights without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * The name and trademarks of copyright holder(s) may NOT be used in + * advertising or publicity pertaining to the Software or any + * derivatives without specific, written prior permission. + */ + +#ifndef NX_MSG_H +#define NX_MSG_H 1 + +#include "datapath.h" + +struct sender; + +int nx_recv_msg(struct datapath *, const struct sender *, const void *); + +#endif /* nx_msg.h */ diff --git a/udatapath/switch-flow.c b/udatapath/switch-flow.c index 82eee55d..5eca39e8 100644 --- a/udatapath/switch-flow.c +++ b/udatapath/switch-flow.c @@ -39,6 +39,7 @@ #include #include "ofpbuf.h" #include "openflow/openflow.h" +#include "openflow/nicira-ext.h" #include "packets.h" #include "timeval.h" @@ -248,14 +249,14 @@ print_flow(const struct sw_flow_key *key) bool flow_timeout(struct sw_flow *flow) { - time_t now = time_now(); + uint64_t now = time_msec(); if (flow->idle_timeout != OFP_FLOW_PERMANENT - && now > flow->used + flow->idle_timeout) { - flow->reason = OFPER_IDLE_TIMEOUT; + && now > flow->used + flow->idle_timeout * 1000) { + flow->reason = NXFER_IDLE_TIMEOUT; return true; } else if (flow->hard_timeout != OFP_FLOW_PERMANENT - && now > flow->created + flow->hard_timeout) { - flow->reason = OFPER_HARD_TIMEOUT; + && now > flow->created + flow->hard_timeout * 1000) { + flow->reason = NXFER_HARD_TIMEOUT; return true; } else { return false; @@ -292,7 +293,18 @@ int flow_has_out_port(struct sw_flow *flow, uint16_t out_port) void flow_used(struct sw_flow *flow, struct ofpbuf *buffer) { - flow->used = time_now(); + flow->used = time_msec(); + + if (flow->key.flow.dl_type == htons(ETH_TYPE_IP)) { + struct ip_header *nh = buffer->l3; + flow->ip_tos = nh->ip_tos; + + if (flow->key.flow.nw_proto == IP_TYPE_TCP) { + struct tcp_header *th = buffer->l4; + flow->tcp_flags |= TCP_FLAGS(th->tcp_ctl); + } + } + flow->packet_count++; flow->byte_count += buffer->size; } diff --git a/udatapath/switch-flow.h b/udatapath/switch-flow.h index ef0497c8..78fc1148 100644 --- a/udatapath/switch-flow.h +++ b/udatapath/switch-flow.h @@ -60,11 +60,14 @@ struct sw_flow { uint16_t priority; /* Only used on entries with wildcards. */ uint16_t idle_timeout; /* Idle time before discarding (seconds). */ uint16_t hard_timeout; /* Hard expiration time (seconds) */ - time_t used; /* Last used time. */ - time_t created; /* When the flow was created. */ + uint64_t used; /* Last used time. */ + uint64_t created; /* When the flow was created. */ uint64_t packet_count; /* Number of packets seen. */ uint64_t byte_count; /* Number of bytes seen. */ - uint8_t reason; /* Reason flow expired (one of OFPER_*). */ + uint8_t reason; /* Reason flow expired (one of NXFER_*). */ + + uint8_t tcp_flags; /* Union of seen TCP flags. */ + uint8_t ip_tos; /* IP TOS value. */ struct sw_flow_actions *sf_acts; diff --git a/udatapath/table-hash.c b/udatapath/table-hash.c index fcdad096..fd1ec8a7 100644 --- a/udatapath/table-hash.c +++ b/udatapath/table-hash.c @@ -36,6 +36,7 @@ #include #include #include +#include "openflow/nicira-ext.h" #include "crc32.h" #include "datapath.h" #include "flow.h" @@ -134,7 +135,7 @@ do_delete(struct sw_flow **bucket) /* Returns number of deleted flows. We can igonre the priority * argument, since all exact-match entries are the same (highest) * priority. */ -static int table_hash_delete(struct sw_table *swt, +static int table_hash_delete(struct datapath *dp, struct sw_table *swt, const struct sw_flow_key *key, uint16_t out_port, uint16_t priority, int strict) @@ -147,6 +148,7 @@ static int table_hash_delete(struct sw_table *swt, struct sw_flow *flow = *bucket; if (flow && !flow_compare(&flow->key.flow, &key->flow) && flow_has_out_port(flow, out_port)) { + dp_send_flow_end(dp, flow, NXFER_DELETE); do_delete(bucket); count = 1; } @@ -158,6 +160,7 @@ static int table_hash_delete(struct sw_table *swt, struct sw_flow *flow = *bucket; if (flow && flow_matches_desc(&flow->key, key, strict) && flow_has_out_port(flow, out_port)) { + dp_send_flow_end(dp, flow, NXFER_DELETE); do_delete(bucket); count++; } @@ -321,15 +324,16 @@ static int table_hash2_modify(struct sw_table *swt, actions, actions_len)); } -static int table_hash2_delete(struct sw_table *swt, +static int table_hash2_delete(struct datapath *dp, struct sw_table *swt, const struct sw_flow_key *key, uint16_t out_port, uint16_t priority, int strict) { struct sw_table_hash2 *t2 = (struct sw_table_hash2 *) swt; - return (table_hash_delete(t2->subtable[0], key, out_port, priority, strict) - + table_hash_delete(t2->subtable[1], key, out_port, priority, - strict)); + return (table_hash_delete(dp, t2->subtable[0], key, out_port, + priority, strict) + + table_hash_delete(dp, t2->subtable[1], key, out_port, + priority, strict)); } static void table_hash2_timeout(struct sw_table *swt, struct list *deleted) diff --git a/udatapath/table-linear.c b/udatapath/table-linear.c index cd22f0f3..ae960ad0 100644 --- a/udatapath/table-linear.c +++ b/udatapath/table-linear.c @@ -37,6 +37,7 @@ #include "flow.h" #include "list.h" #include "openflow/openflow.h" +#include "openflow/nicira-ext.h" #include "switch-flow.h" #include "datapath.h" @@ -126,7 +127,7 @@ do_delete(struct sw_flow *flow) flow_free(flow); } -static int table_linear_delete(struct sw_table *swt, +static int table_linear_delete(struct datapath *dp, struct sw_table *swt, const struct sw_flow_key *key, uint16_t out_port, uint16_t priority, int strict) @@ -139,6 +140,7 @@ static int table_linear_delete(struct sw_table *swt, if (flow_matches_desc(&flow->key, key, strict) && flow_has_out_port(flow, out_port) && (!strict || (flow->priority == priority))) { + dp_send_flow_end(dp, flow, NXFER_DELETE); do_delete(flow); count++; } diff --git a/udatapath/table.h b/udatapath/table.h index 7681c0d3..5d8bc662 100644 --- a/udatapath/table.h +++ b/udatapath/table.h @@ -39,6 +39,7 @@ #include #include +#include "datapath.h" struct sw_flow; struct sw_flow_key; @@ -99,7 +100,8 @@ struct sw_table { * must have that port as an argument for an output action. If * 'strict' is set, wildcards and priority must match. Returns the * number of flows that were deleted. */ - int (*delete)(struct sw_table *table, const struct sw_flow_key *key, + int (*delete)(struct datapath *dp, struct sw_table *table, + const struct sw_flow_key *key, uint16_t out_port, uint16_t priority, int strict); /* Performs timeout processing on all the flow entries in 'table'. -- 2.30.2