+static void
+nxm_put_frag(struct ofpbuf *b, const struct match *match)
+{
+ uint8_t nw_frag = match->flow.nw_frag;
+ uint8_t nw_frag_mask = match->wc.masks.nw_frag;
+
+ switch (nw_frag_mask) {
+ case 0:
+ break;
+
+ case FLOW_NW_FRAG_MASK:
+ nxm_put_8(b, NXM_NX_IP_FRAG, nw_frag);
+ break;
+
+ default:
+ nxm_put_8m(b, NXM_NX_IP_FRAG, nw_frag,
+ nw_frag_mask & FLOW_NW_FRAG_MASK);
+ break;
+ }
+}
+
+/* Appends to 'b' a set of OXM or NXM matches for the IPv4 or IPv6 fields in
+ * 'match'. */
+static void
+nxm_put_ip(struct ofpbuf *b, const struct match *match, enum ofp_version oxm)
+{
+ const struct flow *flow = &match->flow;
+
+ if (flow->dl_type == htons(ETH_TYPE_IP)) {
+ nxm_put_32m(b, mf_oxm_header(MFF_IPV4_SRC, oxm),
+ flow->nw_src, match->wc.masks.nw_src);
+ nxm_put_32m(b, mf_oxm_header(MFF_IPV4_DST, oxm),
+ flow->nw_dst, match->wc.masks.nw_dst);
+ } else {
+ nxm_put_ipv6(b, mf_oxm_header(MFF_IPV6_SRC, oxm),
+ &flow->ipv6_src, &match->wc.masks.ipv6_src);
+ nxm_put_ipv6(b, mf_oxm_header(MFF_IPV6_DST, oxm),
+ &flow->ipv6_dst, &match->wc.masks.ipv6_dst);
+ }
+
+ nxm_put_frag(b, match);
+
+ if (match->wc.masks.nw_tos & IP_DSCP_MASK) {
+ if (oxm) {
+ nxm_put_8(b, mf_oxm_header(MFF_IP_DSCP_SHIFTED, oxm),
+ flow->nw_tos >> 2);
+ } else {
+ nxm_put_8(b, mf_oxm_header(MFF_IP_DSCP, oxm),
+ flow->nw_tos & IP_DSCP_MASK);
+ }
+ }
+
+ if (match->wc.masks.nw_tos & IP_ECN_MASK) {
+ nxm_put_8(b, mf_oxm_header(MFF_IP_ECN, oxm),
+ flow->nw_tos & IP_ECN_MASK);
+ }
+
+ if (!oxm && match->wc.masks.nw_ttl) {
+ nxm_put_8(b, mf_oxm_header(MFF_IP_TTL, oxm), flow->nw_ttl);
+ }
+
+ nxm_put_32m(b, mf_oxm_header(MFF_IPV6_LABEL, oxm),
+ flow->ipv6_label, match->wc.masks.ipv6_label);
+
+ if (match->wc.masks.nw_proto) {
+ nxm_put_8(b, mf_oxm_header(MFF_IP_PROTO, oxm), flow->nw_proto);
+
+ if (flow->nw_proto == IPPROTO_TCP) {
+ nxm_put_16m(b, mf_oxm_header(MFF_TCP_SRC, oxm),
+ flow->tp_src, match->wc.masks.tp_src);
+ nxm_put_16m(b, mf_oxm_header(MFF_TCP_DST, oxm),
+ flow->tp_dst, match->wc.masks.tp_dst);
+ nxm_put_16m(b, mf_oxm_header(MFF_TCP_FLAGS, oxm),
+ flow->tcp_flags, match->wc.masks.tcp_flags);
+ } else if (flow->nw_proto == IPPROTO_UDP) {
+ nxm_put_16m(b, mf_oxm_header(MFF_UDP_SRC, oxm),
+ flow->tp_src, match->wc.masks.tp_src);
+ nxm_put_16m(b, mf_oxm_header(MFF_UDP_DST, oxm),
+ flow->tp_dst, match->wc.masks.tp_dst);
+ } else if (flow->nw_proto == IPPROTO_SCTP) {
+ nxm_put_16m(b, mf_oxm_header(MFF_SCTP_SRC, oxm), flow->tp_src,
+ match->wc.masks.tp_src);
+ nxm_put_16m(b, mf_oxm_header(MFF_SCTP_DST, oxm), flow->tp_dst,
+ match->wc.masks.tp_dst);
+ } else if (is_icmpv4(flow)) {
+ if (match->wc.masks.tp_src) {
+ nxm_put_8(b, mf_oxm_header(MFF_ICMPV4_TYPE, oxm),
+ ntohs(flow->tp_src));
+ }
+ if (match->wc.masks.tp_dst) {
+ nxm_put_8(b, mf_oxm_header(MFF_ICMPV4_CODE, oxm),
+ ntohs(flow->tp_dst));
+ }
+ } else if (is_icmpv6(flow)) {
+ if (match->wc.masks.tp_src) {
+ nxm_put_8(b, mf_oxm_header(MFF_ICMPV6_TYPE, oxm),
+ ntohs(flow->tp_src));
+ }
+ if (match->wc.masks.tp_dst) {
+ nxm_put_8(b, mf_oxm_header(MFF_ICMPV6_CODE, oxm),
+ ntohs(flow->tp_dst));
+ }
+ if (flow->tp_src == htons(ND_NEIGHBOR_SOLICIT) ||
+ flow->tp_src == htons(ND_NEIGHBOR_ADVERT)) {
+ nxm_put_ipv6(b, mf_oxm_header(MFF_ND_TARGET, oxm),
+ &flow->nd_target, &match->wc.masks.nd_target);
+ if (flow->tp_src == htons(ND_NEIGHBOR_SOLICIT)) {
+ nxm_put_eth_masked(b, mf_oxm_header(MFF_ND_SLL, oxm),
+ flow->arp_sha, match->wc.masks.arp_sha);
+ }
+ if (flow->tp_src == htons(ND_NEIGHBOR_ADVERT)) {
+ nxm_put_eth_masked(b, mf_oxm_header(MFF_ND_TLL, oxm),
+ flow->arp_tha, match->wc.masks.arp_tha);
+ }
+ }
+ }
+ }
+}
+
+/* Appends to 'b' the nx_match format that expresses 'match'. For Flow Mod and
+ * Flow Stats Requests messages, a 'cookie' and 'cookie_mask' may be supplied.
+ * Otherwise, 'cookie_mask' should be zero.
+ *
+ * Specify 'oxm' as 0 to express the match in NXM format; otherwise, specify
+ * 'oxm' as the OpenFlow version number for the OXM format to use.