aboutsummaryrefslogtreecommitdiff
path: root/lib/flow.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/flow.c')
-rw-r--r--lib/flow.c280
1 files changed, 280 insertions, 0 deletions
diff --git a/lib/flow.c b/lib/flow.c
new file mode 100644
index 00000000..e55f4fe3
--- /dev/null
+++ b/lib/flow.c
@@ -0,0 +1,280 @@
+/*
+ * Copyright (c) 2008, 2009 Nicira Networks.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <config.h>
+#include <sys/types.h>
+#include "flow.h"
+#include <inttypes.h>
+#include <netinet/in.h>
+#include <stdlib.h>
+#include <string.h>
+#include "coverage.h"
+#include "dynamic-string.h"
+#include "hash.h"
+#include "ofpbuf.h"
+#include "openflow/openflow.h"
+#include "openvswitch/datapath-protocol.h"
+#include "packets.h"
+
+#include "vlog.h"
+#define THIS_MODULE VLM_flow
+
+static struct ip_header *
+pull_ip(struct ofpbuf *packet)
+{
+ if (packet->size >= IP_HEADER_LEN) {
+ struct ip_header *ip = packet->data;
+ int ip_len = IP_IHL(ip->ip_ihl_ver) * 4;
+ if (ip_len >= IP_HEADER_LEN && packet->size >= ip_len) {
+ return ofpbuf_pull(packet, ip_len);
+ }
+ }
+ return NULL;
+}
+
+static struct tcp_header *
+pull_tcp(struct ofpbuf *packet)
+{
+ if (packet->size >= TCP_HEADER_LEN) {
+ struct tcp_header *tcp = packet->data;
+ int tcp_len = TCP_OFFSET(tcp->tcp_ctl) * 4;
+ if (tcp_len >= TCP_HEADER_LEN && packet->size >= tcp_len) {
+ return ofpbuf_pull(packet, tcp_len);
+ }
+ }
+ return NULL;
+}
+
+static struct udp_header *
+pull_udp(struct ofpbuf *packet)
+{
+ return ofpbuf_try_pull(packet, UDP_HEADER_LEN);
+}
+
+static struct icmp_header *
+pull_icmp(struct ofpbuf *packet)
+{
+ return ofpbuf_try_pull(packet, ICMP_HEADER_LEN);
+}
+
+static struct eth_header *
+pull_eth(struct ofpbuf *packet)
+{
+ return ofpbuf_try_pull(packet, ETH_HEADER_LEN);
+}
+
+static struct vlan_header *
+pull_vlan(struct ofpbuf *packet)
+{
+ return ofpbuf_try_pull(packet, VLAN_HEADER_LEN);
+}
+
+/* Returns 1 if 'packet' is an IP fragment, 0 otherwise. */
+int
+flow_extract(struct ofpbuf *packet, uint16_t in_port, flow_t *flow)
+{
+ struct ofpbuf b = *packet;
+ struct eth_header *eth;
+ int retval = 0;
+
+ COVERAGE_INC(flow_extract);
+
+ memset(flow, 0, sizeof *flow);
+ flow->dl_vlan = htons(OFP_VLAN_NONE);
+ flow->in_port = in_port;
+
+ packet->l2 = b.data;
+ packet->l3 = NULL;
+ packet->l4 = NULL;
+ packet->l7 = NULL;
+
+ eth = pull_eth(&b);
+ if (eth) {
+ if (ntohs(eth->eth_type) >= OFP_DL_TYPE_ETH2_CUTOFF) {
+ /* This is an Ethernet II frame */
+ flow->dl_type = eth->eth_type;
+ } else {
+ /* This is an 802.2 frame */
+ struct llc_header *llc = ofpbuf_at(&b, 0, sizeof *llc);
+ struct snap_header *snap = ofpbuf_at(&b, sizeof *llc,
+ sizeof *snap);
+ if (llc == NULL) {
+ return 0;
+ }
+ if (snap
+ && llc->llc_dsap == LLC_DSAP_SNAP
+ && llc->llc_ssap == LLC_SSAP_SNAP
+ && llc->llc_cntl == LLC_CNTL_SNAP
+ && !memcmp(snap->snap_org, SNAP_ORG_ETHERNET,
+ sizeof snap->snap_org)) {
+ flow->dl_type = snap->snap_type;
+ ofpbuf_pull(&b, LLC_SNAP_HEADER_LEN);
+ } else {
+ flow->dl_type = htons(OFP_DL_TYPE_NOT_ETH_TYPE);
+ ofpbuf_pull(&b, sizeof(struct llc_header));
+ }
+ }
+
+ /* Check for a VLAN tag */
+ if (flow->dl_type == htons(ETH_TYPE_VLAN)) {
+ struct vlan_header *vh = pull_vlan(&b);
+ if (vh) {
+ flow->dl_type = vh->vlan_next_type;
+ flow->dl_vlan = vh->vlan_tci & htons(VLAN_VID_MASK);
+ }
+ }
+ memcpy(flow->dl_src, eth->eth_src, ETH_ADDR_LEN);
+ memcpy(flow->dl_dst, eth->eth_dst, ETH_ADDR_LEN);
+
+ packet->l3 = b.data;
+ if (flow->dl_type == htons(ETH_TYPE_IP)) {
+ const struct ip_header *nh = pull_ip(&b);
+ if (nh) {
+ flow->nw_src = nh->ip_src;
+ flow->nw_dst = nh->ip_dst;
+ flow->nw_proto = nh->ip_proto;
+ packet->l4 = b.data;
+ if (!IP_IS_FRAGMENT(nh->ip_frag_off)) {
+ if (flow->nw_proto == IP_TYPE_TCP) {
+ const struct tcp_header *tcp = pull_tcp(&b);
+ if (tcp) {
+ flow->tp_src = tcp->tcp_src;
+ flow->tp_dst = tcp->tcp_dst;
+ packet->l7 = b.data;
+ } else {
+ /* Avoid tricking other code into thinking that
+ * this packet has an L4 header. */
+ flow->nw_proto = 0;
+ }
+ } else if (flow->nw_proto == IP_TYPE_UDP) {
+ const struct udp_header *udp = pull_udp(&b);
+ if (udp) {
+ flow->tp_src = udp->udp_src;
+ flow->tp_dst = udp->udp_dst;
+ packet->l7 = b.data;
+ } else {
+ /* Avoid tricking other code into thinking that
+ * this packet has an L4 header. */
+ flow->nw_proto = 0;
+ }
+ } else if (flow->nw_proto == IP_TYPE_ICMP) {
+ const struct icmp_header *icmp = pull_icmp(&b);
+ if (icmp) {
+ flow->icmp_type = htons(icmp->icmp_type);
+ flow->icmp_code = htons(icmp->icmp_code);
+ packet->l7 = b.data;
+ } else {
+ /* Avoid tricking other code into thinking that
+ * this packet has an L4 header. */
+ flow->nw_proto = 0;
+ }
+ }
+ } else {
+ retval = 1;
+ }
+ }
+ }
+ }
+ return retval;
+}
+
+/* Extracts the flow stats for a packet. The 'flow' and 'packet'
+ * arguments must have been initialized through a call to flow_extract().
+ */
+void
+flow_extract_stats(const flow_t *flow, struct ofpbuf *packet,
+ struct odp_flow_stats *stats)
+{
+ memset(stats, '\0', sizeof(*stats));
+
+ if ((flow->dl_type == htons(ETH_TYPE_IP)) && packet->l4) {
+ struct ip_header *ip = packet->l3;
+ stats->ip_tos = ip->ip_tos;
+ if ((flow->nw_proto == IP_TYPE_TCP) && packet->l7) {
+ struct tcp_header *tcp = packet->l4;
+ stats->tcp_flags = TCP_FLAGS(tcp->tcp_ctl);
+ }
+ }
+
+ stats->n_bytes = packet->size;
+ stats->n_packets = 1;
+}
+
+void
+flow_to_match(const flow_t *flow, uint32_t wildcards, struct ofp_match *match)
+{
+ match->wildcards = htonl(wildcards);
+ match->in_port = htons(flow->in_port == ODPP_LOCAL ? OFPP_LOCAL
+ : flow->in_port);
+ match->dl_vlan = flow->dl_vlan;
+ memcpy(match->dl_src, flow->dl_src, ETH_ADDR_LEN);
+ memcpy(match->dl_dst, flow->dl_dst, ETH_ADDR_LEN);
+ match->dl_type = flow->dl_type;
+ match->nw_src = flow->nw_src;
+ match->nw_dst = flow->nw_dst;
+ match->nw_proto = flow->nw_proto;
+ match->tp_src = flow->tp_src;
+ match->tp_dst = flow->tp_dst;
+ match->pad = 0;
+}
+
+void
+flow_from_match(flow_t *flow, uint32_t *wildcards,
+ const struct ofp_match *match)
+{
+ if (wildcards) {
+ *wildcards = ntohl(match->wildcards);
+ }
+ flow->nw_src = match->nw_src;
+ flow->nw_dst = match->nw_dst;
+ flow->in_port = (match->in_port == htons(OFPP_LOCAL) ? ODPP_LOCAL
+ : ntohs(match->in_port));
+ flow->dl_vlan = match->dl_vlan;
+ flow->dl_type = match->dl_type;
+ flow->tp_src = match->tp_src;
+ flow->tp_dst = match->tp_dst;
+ memcpy(flow->dl_src, match->dl_src, ETH_ADDR_LEN);
+ memcpy(flow->dl_dst, match->dl_dst, ETH_ADDR_LEN);
+ flow->nw_proto = match->nw_proto;
+ flow->reserved = 0;
+}
+
+char *
+flow_to_string(const flow_t *flow)
+{
+ struct ds ds = DS_EMPTY_INITIALIZER;
+ flow_format(&ds, flow);
+ return ds_cstr(&ds);
+}
+
+void
+flow_format(struct ds *ds, const flow_t *flow)
+{
+ ds_put_format(ds, "port%04x:vlan%d mac"ETH_ADDR_FMT"->"ETH_ADDR_FMT" "
+ "type%04x proto%"PRId8" ip"IP_FMT"->"IP_FMT" port%d->%d",
+ flow->in_port, ntohs(flow->dl_vlan),
+ ETH_ADDR_ARGS(flow->dl_src), ETH_ADDR_ARGS(flow->dl_dst),
+ ntohs(flow->dl_type), flow->nw_proto,
+ IP_ARGS(&flow->nw_src), IP_ARGS(&flow->nw_dst),
+ ntohs(flow->tp_src), ntohs(flow->tp_dst));
+}
+
+void
+flow_print(FILE *stream, const flow_t *flow)
+{
+ char *s = flow_to_string(flow);
+ fputs(s, stream);
+ free(s);
+}