-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
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;
#include <net/genetlink.h>
#include <linux/ip.h>
#include <linux/delay.h>
+#include <linux/time.h>
#include <linux/etherdevice.h>
#include <linux/kernel.h>
#include <linux/kthread.h>
#include <linux/ethtool.h>
#include <linux/random.h>
#include <asm/system.h>
+#include <asm/div64.h>
#include <linux/netfilter_bridge.h>
#include <linux/netfilter_ipv4.h>
#include <linux/inetdevice.h>
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,
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)
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);
#include <linux/workqueue.h>
#include <linux/skbuff.h>
#include "openflow/openflow.h"
+#include "openflow/nicira-ext.h"
#include "flow.h"
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. */
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);
#include <linux/if_ether.h>
#include <linux/if_vlan.h>
#include <net/llc_pdu.h>
-#include <linux/ip.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/llc.h>
#include <linux/module.h>
-#include <linux/tcp.h>
-#include <linux/udp.h>
-#include <linux/icmp.h>
#include <linux/in.h>
#include <linux/rcupdate.h>
-#include <net/ip.h>
#include "openflow/openflow.h"
+#include "openflow/nicira-ext.h"
#include "compat.h"
struct kmem_cache *flow_cache;
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;
}
&& !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. */
#include <linux/spinlock.h>
#include <linux/list.h>
#include <linux/types.h>
+#include <linux/jiffies.h>
#include <linux/rcupdate.h>
#include <linux/gfp.h>
#include <linux/skbuff.h>
#include <linux/if_ether.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
+#include <linux/icmp.h>
+#include <net/ip.h>
#include "openflow/openflow.h"
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;
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;
};
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);
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);
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;
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 \
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
--- /dev/null
+#ifndef __LINUX_TIME_WRAPPER_H
+#define __LINUX_TIME_WRAPPER_H 1
+
+#include_next <linux/time.h>
+
+/* 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
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;
/* 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)
{
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;
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;
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);
}
}
}
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));
}
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)
{
&& 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;
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;
* 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'.
/* 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 {
};
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 */
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,
}
}
+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;
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'
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)
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 \
--- /dev/null
+/* 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 <config.h>
+#include "flow-end.h"
+#include <errno.h>
+#include <arpa/inet.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <time.h>
+#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);
+}
--- /dev/null
+/* 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 */
--- /dev/null
+/* 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 <util.h>
+
+
+#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 */
\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
#ifdef SUPPORT_SNAT
#include "snat.h"
#endif
+#include "flow-end.h"
#include "stp-secchan.h"
#include "status.h"
#include "timeval.h"
#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);
}
OPT_IN_BAND,
OPT_COMMAND_ACL,
OPT_COMMAND_DIR,
+ OPT_NETFLOW,
VLOG_OPTION_ENUMS
};
static struct option long_options[] = {
{"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'},
s->in_band = true;
s->command_acl = "";
s->command_dir = xasprintf("%s/commands", ofp_pkgdatadir);
+ s->netflow_dst = NULL;
for (;;) {
int c;
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,
" --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"
/* 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 {
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 \
/* 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))) {
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;
struct sw_flow_key;
struct ofp_action_header;
struct list;
+struct datapath;
#define TABLE_LINEAR_MAX_FLOWS 100
#define TABLE_HASH_MAX_FLOWS 65536
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 *,
#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
| (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. */
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 *);
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);
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);
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);
}
}
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);
}
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;
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);
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. */
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 {
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;
};
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))
{
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
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);
#include <stdbool.h>
#include <stdint.h>
+#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);
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,
--- /dev/null
+/* 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 <errno.h>
+#include <arpa/inet.h>
+#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;
+}
--- /dev/null
+/* 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 */
#include <string.h>
#include "ofpbuf.h"
#include "openflow/openflow.h"
+#include "openflow/nicira-ext.h"
#include "packets.h"
#include "timeval.h"
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;
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;
}
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;
#include <assert.h>
#include <stdlib.h>
#include <string.h>
+#include "openflow/nicira-ext.h"
#include "crc32.h"
#include "datapath.h"
#include "flow.h"
/* 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)
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;
}
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++;
}
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)
#include "flow.h"
#include "list.h"
#include "openflow/openflow.h"
+#include "openflow/nicira-ext.h"
#include "switch-flow.h"
#include "datapath.h"
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)
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++;
}
#include <stddef.h>
#include <stdint.h>
+#include "datapath.h"
struct sw_flow;
struct sw_flow_key;
* 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'.