#include <config.h>
#include <sys/types.h>
#include "flow.h"
+#include <assert.h>
#include <errno.h>
#include <inttypes.h>
#include <netinet/in.h>
#include <string.h>
#include "byte-order.h"
#include "coverage.h"
-#include "dpif.h"
#include "dynamic-string.h"
#include "hash.h"
#include "ofpbuf.h"
#include "openflow/openflow.h"
-#include "openvswitch/datapath-protocol.h"
#include "packets.h"
#include "unaligned.h"
#include "vlog.h"
return retval;
}
-/* Extracts the flow stats for a packet. The 'flow' and 'packet'
- * arguments must have been initialized through a call to flow_extract().
- */
+/* For every bit of a field that is wildcarded in 'wildcards', sets the
+ * corresponding bit in 'flow' to zero. */
void
-flow_extract_stats(const struct flow *flow, struct ofpbuf *packet,
- struct dpif_flow_stats *stats)
+flow_zero_wildcards(struct flow *flow, const struct flow_wildcards *wildcards)
{
- memset(stats, 0, sizeof(*stats));
+ const flow_wildcards_t wc = wildcards->wildcards;
+ int i;
- if ((flow->dl_type == htons(ETH_TYPE_IP)) && packet->l4) {
- if ((flow->nw_proto == IPPROTO_TCP) && packet->l7) {
- struct tcp_header *tcp = packet->l4;
- stats->tcp_flags = TCP_FLAGS(tcp->tcp_ctl);
- }
- }
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 1);
- stats->n_bytes = packet->size;
- stats->n_packets = 1;
+ for (i = 0; i < FLOW_N_REGS; i++) {
+ flow->regs[i] &= wildcards->reg_masks[i];
+ }
+ flow->tun_id &= wildcards->tun_id_mask;
+ flow->nw_src &= wildcards->nw_src_mask;
+ flow->nw_dst &= wildcards->nw_dst_mask;
+ if (wc & FWW_IN_PORT) {
+ flow->in_port = 0;
+ }
+ flow->vlan_tci &= wildcards->vlan_tci_mask;
+ if (wc & FWW_DL_TYPE) {
+ flow->dl_type = 0;
+ }
+ if (wc & FWW_TP_SRC) {
+ flow->tp_src = 0;
+ }
+ if (wc & FWW_TP_DST) {
+ flow->tp_dst = 0;
+ }
+ if (wc & FWW_DL_SRC) {
+ memset(flow->dl_src, 0, sizeof flow->dl_src);
+ }
+ if (wc & FWW_DL_DST) {
+ flow->dl_dst[0] &= 0x01;
+ memset(&flow->dl_dst[1], 0, 5);
+ }
+ if (wc & FWW_ETH_MCAST) {
+ flow->dl_dst[0] &= 0xfe;
+ }
+ if (wc & FWW_NW_PROTO) {
+ flow->nw_proto = 0;
+ }
+ if (wc & FWW_NW_TOS) {
+ flow->nw_tos = 0;
+ }
+ if (wc & FWW_ARP_SHA) {
+ memset(flow->arp_sha, 0, sizeof flow->arp_sha);
+ }
+ if (wc & FWW_ARP_THA) {
+ memset(flow->arp_tha, 0, sizeof flow->arp_tha);
+ }
+ flow->ipv6_src = ipv6_addr_bitand(&flow->ipv6_src,
+ &wildcards->ipv6_src_mask);
+ flow->ipv6_dst = ipv6_addr_bitand(&flow->ipv6_dst,
+ &wildcards->ipv6_dst_mask);
+ if (wc & FWW_ND_TARGET) {
+ memset(&flow->nd_target, 0, sizeof flow->nd_target);
+ }
}
char *
flow_format(struct ds *ds, const struct flow *flow)
{
ds_put_format(ds, "tunnel%#"PRIx64":in_port%04"PRIx16":tci(",
- flow->tun_id, flow->in_port);
+ ntohll(flow->tun_id), flow->in_port);
if (flow->vlan_tci) {
ds_put_format(ds, "vlan%"PRIu16",pcp%d",
vlan_tci_to_vid(flow->vlan_tci),
print_ipv6_addr(ds, &flow->ipv6_src);
ds_put_cstr(ds, "->");
print_ipv6_addr(ds, &flow->ipv6_dst);
-
+
} else {
ds_put_format(ds, " proto%"PRIu8
" tos%"PRIu8
{
int i;
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 1);
+
if (wc->wildcards
|| wc->tun_id_mask != htonll(UINT64_MAX)
|| wc->nw_src_mask != htonl(UINT32_MAX)
}
for (i = 0; i < FLOW_N_REGS; i++) {
- if (wc->reg_masks[i] != htonl(UINT32_MAX)) {
+ if (wc->reg_masks[i] != UINT32_MAX) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/* Returns true if 'wc' matches every packet, false if 'wc' fixes any bits or
+ * fields. */
+bool
+flow_wildcards_is_catchall(const struct flow_wildcards *wc)
+{
+ int i;
+
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 1);
+
+ if (wc->wildcards != FWW_ALL
+ || wc->tun_id_mask != htonll(0)
+ || wc->nw_src_mask != htonl(0)
+ || wc->nw_dst_mask != htonl(0)
+ || wc->vlan_tci_mask != htons(0)
+ || !ipv6_mask_is_any(&wc->ipv6_src_mask)
+ || !ipv6_mask_is_any(&wc->ipv6_dst_mask)) {
+ return false;
+ }
+
+ for (i = 0; i < FLOW_N_REGS; i++) {
+ if (wc->reg_masks[i] != 0) {
return false;
}
}
/* Returns a hash of the wildcards in 'wc'. */
uint32_t
-flow_wildcards_hash(const struct flow_wildcards *wc)
+flow_wildcards_hash(const struct flow_wildcards *wc, uint32_t basis)
{
/* If you change struct flow_wildcards and thereby trigger this
* assertion, please check that the new struct flow_wildcards has no holes
* in it before you update the assertion. */
BUILD_ASSERT_DECL(sizeof *wc == 56 + FLOW_N_REGS * 4);
- return hash_bytes(wc, sizeof *wc, 0);
+ return hash_bytes(wc, sizeof *wc, basis);
}
/* Returns true if 'a' and 'b' represent the same wildcards, false if they are
|| a->tun_id_mask != b->tun_id_mask
|| a->nw_src_mask != b->nw_src_mask
|| a->nw_dst_mask != b->nw_dst_mask
- || a->vlan_tci_mask != b->vlan_tci_mask
+ || a->vlan_tci_mask != b->vlan_tci_mask
|| !ipv6_addr_equals(&a->ipv6_src_mask, &b->ipv6_src_mask)
|| !ipv6_addr_equals(&a->ipv6_dst_mask, &b->ipv6_dst_mask)) {
return false;
wc->reg_masks[idx] = mask;
}
+/* Returns the wildcard bitmask for the Ethernet destination address
+ * that 'wc' specifies. The bitmask has a 0 in each bit that is wildcarded
+ * and a 1 in each bit that must match. */
+const uint8_t *
+flow_wildcards_to_dl_dst_mask(flow_wildcards_t wc)
+{
+ static const uint8_t no_wild[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+ static const uint8_t addr_wild[] = {0x01, 0x00, 0x00, 0x00, 0x00, 0x00};
+ static const uint8_t mcast_wild[] = {0xfe, 0xff, 0xff, 0xff, 0xff, 0xff};
+ static const uint8_t all_wild[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+ switch (wc & (FWW_DL_DST | FWW_ETH_MCAST)) {
+ case 0: return no_wild;
+ case FWW_DL_DST: return addr_wild;
+ case FWW_ETH_MCAST: return mcast_wild;
+ case FWW_DL_DST | FWW_ETH_MCAST: return all_wild;
+ }
+ NOT_REACHED();
+}
+
+/* Returns true if 'mask' is a valid wildcard bitmask for the Ethernet
+ * destination address. Valid bitmasks are either all-bits-0 or all-bits-1,
+ * except that the multicast bit may differ from the rest of the bits. So,
+ * there are four possible valid bitmasks:
+ *
+ * - 00:00:00:00:00:00
+ * - 01:00:00:00:00:00
+ * - fe:ff:ff:ff:ff:ff
+ * - ff:ff:ff:ff:ff:ff
+ *
+ * All other bitmasks are invalid. */
+bool
+flow_wildcards_is_dl_dst_mask_valid(const uint8_t mask[ETH_ADDR_LEN])
+{
+ switch (mask[0]) {
+ case 0x00:
+ case 0x01:
+ return (mask[1] | mask[2] | mask[3] | mask[4] | mask[5]) == 0x00;
+
+ case 0xfe:
+ case 0xff:
+ return (mask[1] & mask[2] & mask[3] & mask[4] & mask[5]) == 0xff;
+
+ default:
+ return false;
+ }
+}
+
+/* Returns 'wc' with the FWW_DL_DST and FWW_ETH_MCAST bits modified
+ * appropriately to match 'mask'.
+ *
+ * This function will assert-fail if 'mask' is invalid. Only 'mask' values
+ * accepted by flow_wildcards_is_dl_dst_mask_valid() are allowed. */
+flow_wildcards_t
+flow_wildcards_set_dl_dst_mask(flow_wildcards_t wc,
+ const uint8_t mask[ETH_ADDR_LEN])
+{
+ assert(flow_wildcards_is_dl_dst_mask_valid(mask));
+
+ switch (mask[0]) {
+ case 0x00:
+ return wc | FWW_DL_DST | FWW_ETH_MCAST;
+
+ case 0x01:
+ return (wc | FWW_DL_DST) & ~FWW_ETH_MCAST;
+
+ case 0xfe:
+ return (wc & ~FWW_DL_DST) | FWW_ETH_MCAST;
+
+ case 0xff:
+ return wc & ~(FWW_DL_DST | FWW_ETH_MCAST);
+
+ default:
+ NOT_REACHED();
+ }
+}
+
/* Hashes 'flow' based on its L2 through L4 protocol information. */
uint32_t
flow_hash_symmetric_l4(const struct flow *flow, uint32_t basis)
}
fields.vlan_tci = flow->vlan_tci & htons(VLAN_VID_MASK);
fields.eth_type = flow->dl_type;
+
+ /* UDP source and destination port are not taken into account because they
+ * will not necessarily be symmetric in a bidirectional flow. */
if (fields.eth_type == htons(ETH_TYPE_IP)) {
fields.ipv4_addr = flow->nw_src ^ flow->nw_dst;
fields.ip_proto = flow->nw_proto;
- if (fields.ip_proto == IPPROTO_TCP || fields.ip_proto == IPPROTO_UDP) {
+ if (fields.ip_proto == IPPROTO_TCP) {
fields.tp_addr = flow->tp_src ^ flow->tp_dst;
}
} else if (fields.eth_type == htons(ETH_TYPE_IPV6)) {
ipv6_addr[i] = a[i] ^ b[i];
}
fields.ip_proto = flow->nw_proto;
- if (fields.ip_proto == IPPROTO_TCP || fields.ip_proto == IPPROTO_UDP) {
+ if (fields.ip_proto == IPPROTO_TCP) {
fields.tp_addr = flow->tp_src ^ flow->tp_dst;
}
}
return hash_bytes(&fields, sizeof fields, basis);
}
+
+/* Hashes the portions of 'flow' designated by 'fields'. */
+uint32_t
+flow_hash_fields(const struct flow *flow, enum nx_hash_fields fields,
+ uint16_t basis)
+{
+ switch (fields) {
+
+ case NX_HASH_FIELDS_ETH_SRC:
+ return hash_bytes(flow->dl_src, sizeof flow->dl_src, basis);
+
+ case NX_HASH_FIELDS_SYMMETRIC_L4:
+ return flow_hash_symmetric_l4(flow, basis);
+ }
+
+ NOT_REACHED();
+}
+
+/* Returns a string representation of 'fields'. */
+const char *
+flow_hash_fields_to_str(enum nx_hash_fields fields)
+{
+ switch (fields) {
+ case NX_HASH_FIELDS_ETH_SRC: return "eth_src";
+ case NX_HASH_FIELDS_SYMMETRIC_L4: return "symmetric_l4";
+ default: return "<unknown>";
+ }
+}
+
+/* Returns true if the value of 'fields' is supported. Otherwise false. */
+bool
+flow_hash_fields_valid(enum nx_hash_fields fields)
+{
+ return fields == NX_HASH_FIELDS_ETH_SRC
+ || fields == NX_HASH_FIELDS_SYMMETRIC_L4;
+}
+
+/* Puts into 'b' a packet that flow_extract() would parse as having the given
+ * 'flow'.
+ *
+ * (This is useful only for testing, obviously, and the packet isn't really
+ * valid. It hasn't got any checksums filled in, for one, and lots of fields
+ * are just zeroed.) */
+void
+flow_compose(struct ofpbuf *b, const struct flow *flow)
+{
+ eth_compose(b, flow->dl_dst, flow->dl_src, ntohs(flow->dl_type), 0);
+ if (flow->dl_type == htons(FLOW_DL_TYPE_NONE)) {
+ struct eth_header *eth = b->l2;
+ eth->eth_type = htons(b->size);
+ return;
+ }
+
+ if (flow->vlan_tci & htons(VLAN_CFI)) {
+ eth_push_vlan(b, flow->vlan_tci & ~htons(VLAN_CFI));
+ }
+
+ if (flow->dl_type == htons(ETH_TYPE_IP)) {
+ struct ip_header *ip;
+
+ b->l3 = ip = ofpbuf_put_zeros(b, sizeof *ip);
+ ip->ip_ihl_ver = IP_IHL_VER(5, 4);
+ ip->ip_tos = flow->nw_tos;
+ ip->ip_proto = flow->nw_proto;
+ ip->ip_src = flow->nw_src;
+ ip->ip_dst = flow->nw_dst;
+
+ if (flow->nw_proto == IPPROTO_TCP) {
+ struct tcp_header *tcp;
+
+ b->l4 = tcp = ofpbuf_put_zeros(b, sizeof *tcp);
+ tcp->tcp_src = flow->tp_src;
+ tcp->tcp_dst = flow->tp_dst;
+ } else if (flow->nw_proto == IPPROTO_UDP) {
+ struct udp_header *udp;
+
+ b->l4 = udp = ofpbuf_put_zeros(b, sizeof *udp);
+ udp->udp_src = flow->tp_src;
+ udp->udp_dst = flow->tp_dst;
+ } else if (flow->nw_proto == IPPROTO_ICMP) {
+ struct icmp_header *icmp;
+
+ b->l4 = icmp = ofpbuf_put_zeros(b, sizeof *icmp);
+ icmp->icmp_type = ntohs(flow->tp_src);
+ icmp->icmp_code = ntohs(flow->tp_dst);
+ }
+ } else if (flow->dl_type == htons(ETH_TYPE_IPV6)) {
+ /* XXX */
+ } else if (flow->dl_type == htons(ETH_TYPE_ARP)) {
+ struct arp_eth_header *arp;
+
+ b->l3 = arp = ofpbuf_put_zeros(b, sizeof *arp);
+ arp->ar_hrd = htons(1);
+ arp->ar_pro = htons(ETH_TYPE_IP);
+ arp->ar_hln = ETH_ADDR_LEN;
+ arp->ar_pln = 4;
+ arp->ar_op = htons(flow->nw_proto);
+
+ if (flow->nw_proto == ARP_OP_REQUEST ||
+ flow->nw_proto == ARP_OP_REPLY) {
+ arp->ar_spa = flow->nw_src;
+ arp->ar_tpa = flow->nw_dst;
+ memcpy(arp->ar_sha, flow->arp_sha, ETH_ADDR_LEN);
+ memcpy(arp->ar_tha, flow->arp_tha, ETH_ADDR_LEN);
+ }
+ }
+}