flow: extend it to carry IGMP protocol information
[cascardo/ovs.git] / lib / flow.c
index 2df7b3d..5ee5b3f 100644 (file)
@@ -277,7 +277,7 @@ parse_icmpv6(void **datap, size_t *sizep, const struct icmp6_hdr *icmp,
         (icmp->icmp6_type == ND_NEIGHBOR_SOLICIT ||
          icmp->icmp6_type == ND_NEIGHBOR_ADVERT)) {
 
-        *nd_target = data_try_pull(datap, sizep, sizeof *nd_target);
+        *nd_target = data_try_pull(datap, sizep, sizeof **nd_target);
         if (OVS_UNLIKELY(!*nd_target)) {
             return false;
         }
@@ -598,6 +598,15 @@ miniflow_extract(struct ofpbuf *packet, const struct pkt_metadata *md,
                 miniflow_push_be16(mf, tp_src, htons(icmp->icmp_type));
                 miniflow_push_be16(mf, tp_dst, htons(icmp->icmp_code));
             }
+        } else if (OVS_LIKELY(nw_proto == IPPROTO_IGMP)) {
+            if (OVS_LIKELY(size >= IGMP_HEADER_LEN)) {
+                const struct igmp_header *igmp = data;
+
+                miniflow_push_be16(mf, tp_src, htons(igmp->igmp_type));
+                miniflow_push_be16(mf, tp_dst, htons(igmp->igmp_code));
+                miniflow_push_be32(mf, igmp_group_ip4,
+                                   get_16aligned_be32(&igmp->group));
+            }
         } else if (OVS_LIKELY(nw_proto == IPPROTO_ICMPV6)) {
             if (OVS_LIKELY(size >= sizeof(struct icmp6_hdr))) {
                 const struct in6_addr *nd_target = NULL;
@@ -607,12 +616,12 @@ miniflow_extract(struct ofpbuf *packet, const struct pkt_metadata *md,
                 memset(arp_buf, 0, sizeof arp_buf);
                 if (OVS_LIKELY(parse_icmpv6(&data, &size, icmp, &nd_target,
                                             arp_buf))) {
+                    miniflow_push_words(mf, arp_sha, arp_buf,
+                                             ETH_ADDR_LEN * 2 / 4);
                     if (nd_target) {
                         miniflow_push_words(mf, nd_target, nd_target,
                                             sizeof *nd_target / 4);
                     }
-                    miniflow_push_words(mf, arp_sha, arp_buf,
-                                             ETH_ADDR_LEN * 2 / 4);
                     miniflow_push_be16(mf, tp_src, htons(icmp->icmp6_type));
                     miniflow_push_be16(mf, tp_dst, htons(icmp->icmp6_code));
                 }
@@ -656,7 +665,7 @@ flow_unwildcard_tp_ports(const struct flow *flow, struct flow_wildcards *wc)
 void
 flow_get_metadata(const struct flow *flow, struct flow_metadata *fmd)
 {
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 26);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 27);
 
     fmd->dp_hash = flow->dp_hash;
     fmd->recirc_id = flow->recirc_id;
@@ -687,6 +696,8 @@ flow_tun_flag_to_string(uint32_t flags)
         return "csum";
     case FLOW_TNL_F_KEY:
         return "key";
+    case FLOW_TNL_F_OAM:
+        return "oam";
     default:
         return NULL;
     }
@@ -1316,7 +1327,7 @@ flow_push_mpls(struct flow *flow, int n, ovs_be16 mpls_eth_type,
         flow->mpls_lse[0] = set_mpls_lse_values(ttl, tc, 1, htonl(label));
 
         /* Clear all L3 and L4 fields. */
-        BUILD_ASSERT(FLOW_WC_SEQ == 26);
+        BUILD_ASSERT(FLOW_WC_SEQ == 27);
         memset((char *) flow + FLOW_SEGMENT_2_ENDS_AT, 0,
                sizeof(struct flow) - FLOW_SEGMENT_2_ENDS_AT);
     }
@@ -1427,6 +1438,15 @@ flow_compose_l4(struct ofpbuf *b, const struct flow *flow)
             icmp->icmp_type = ntohs(flow->tp_src);
             icmp->icmp_code = ntohs(flow->tp_dst);
             icmp->icmp_csum = csum(icmp, ICMP_HEADER_LEN);
+        } else if (flow->nw_proto == IPPROTO_IGMP) {
+            struct igmp_header *igmp;
+
+            l4_len = sizeof *igmp;
+            igmp = ofpbuf_put_zeros(b, l4_len);
+            igmp->igmp_type = ntohs(flow->tp_src);
+            igmp->igmp_code = ntohs(flow->tp_dst);
+            put_16aligned_be32(&igmp->group, flow->igmp_group_ip4);
+            igmp->igmp_csum = csum(igmp, IGMP_HEADER_LEN);
         } else if (flow->nw_proto == IPPROTO_ICMPV6) {
             struct icmp6_hdr *icmp;
 
@@ -1512,6 +1532,7 @@ flow_compose(struct ofpbuf *b, const struct flow *flow)
 
         l4_len = flow_compose_l4(b, flow);
 
+        ip = ofpbuf_l3(b);
         ip->ip_tot_len = htons(b->l4_ofs - b->l3_ofs + l4_len);
         ip->ip_csum = csum(ip, sizeof *ip);
     } else if (flow->dl_type == htons(ETH_TYPE_IPV6)) {
@@ -1530,6 +1551,7 @@ flow_compose(struct ofpbuf *b, const struct flow *flow)
 
         l4_len = flow_compose_l4(b, flow);
 
+        nh = ofpbuf_l3(b);
         nh->ip6_plen = htons(l4_len);
     } else if (flow->dl_type == htons(ETH_TYPE_ARP) ||
                flow->dl_type == htons(ETH_TYPE_RARP)) {
@@ -1578,13 +1600,15 @@ miniflow_n_values(const struct miniflow *flow)
 static uint32_t *
 miniflow_alloc_values(struct miniflow *flow, int n)
 {
-    if (n <= sizeof flow->inline_values / sizeof(uint32_t)) {
+    int size = MINIFLOW_VALUES_SIZE(n);
+
+    if (size <= sizeof flow->inline_values) {
         flow->values_inline = true;
         return flow->inline_values;
     } else {
         COVERAGE_INC(miniflow_malloc);
         flow->values_inline = false;
-        flow->offline_values = xmalloc(n * sizeof(uint32_t));
+        flow->offline_values = xmalloc(size);
         return flow->offline_values;
     }
 }
@@ -1652,18 +1676,57 @@ miniflow_init_with_minimask(struct miniflow *dst, const struct flow *src,
 void
 miniflow_clone(struct miniflow *dst, const struct miniflow *src)
 {
-    int n = miniflow_n_values(src);
+    int size = MINIFLOW_VALUES_SIZE(miniflow_n_values(src));
+    uint32_t *values;
+
     dst->map = src->map;
-    memcpy(miniflow_alloc_values(dst, n), miniflow_get_values(src),
-           n * sizeof(uint32_t));
+    if (size <= sizeof dst->inline_values) {
+        dst->values_inline = true;
+        values = dst->inline_values;
+    } else {
+        dst->values_inline = false;
+        COVERAGE_INC(miniflow_malloc);
+        dst->offline_values = xmalloc(size);
+        values = dst->offline_values;
+    }
+    memcpy(values, miniflow_get_values(src), size);
+}
+
+/* Initializes 'dst' as a copy of 'src'.  The caller must have allocated
+ * 'dst' to have inline space all data in 'src'. */
+void
+miniflow_clone_inline(struct miniflow *dst, const struct miniflow *src,
+                      size_t n_values)
+{
+    dst->values_inline = true;
+    dst->map = src->map;
+    memcpy(dst->inline_values, miniflow_get_values(src),
+           MINIFLOW_VALUES_SIZE(n_values));
 }
 
 /* Initializes 'dst' with the data in 'src', destroying 'src'.
- * The caller must eventually free 'dst' with miniflow_destroy(). */
+ * The caller must eventually free 'dst' with miniflow_destroy().
+ * 'dst' must be regularly sized miniflow, but 'src' can have
+ * larger than default inline values. */
 void
 miniflow_move(struct miniflow *dst, struct miniflow *src)
 {
-    *dst = *src;
+    int size = MINIFLOW_VALUES_SIZE(miniflow_n_values(src));
+
+    dst->map = src->map;
+    if (size <= sizeof dst->inline_values) {
+        dst->values_inline = true;
+        memcpy(dst->inline_values, miniflow_get_values(src), size);
+        miniflow_destroy(src);
+    } else if (src->values_inline) {
+        dst->values_inline = false;
+        COVERAGE_INC(miniflow_malloc);
+        dst->offline_values = xmalloc(size);
+        memcpy(dst->offline_values, src->inline_values, size);
+    } else {
+        dst->values_inline = false;
+        dst->offline_values = src->offline_values;
+    }
 }
 
 /* Frees any memory owned by 'flow'.  Does not free the storage in which 'flow'
@@ -1707,11 +1770,7 @@ miniflow_equal(const struct miniflow *a, const struct miniflow *b)
     if (OVS_LIKELY(a_map == b_map)) {
         int count = miniflow_n_values(a);
 
-        while (count--) {
-            if (*ap++ != *bp++) {
-                return false;
-            }
-        }
+        return !memcmp(ap, bp, count * sizeof *ap);
     } else {
         uint64_t map;