+/* Returns the number of MPLS LSEs present in 'flow'
+ *
+ * Returns 0 if the 'dl_type' of 'flow' is not an MPLS ethernet type.
+ * Otherwise traverses 'flow''s MPLS label stack stopping at the
+ * first entry that has the BoS bit set. If no such entry exists then
+ * the maximum number of LSEs that can be stored in 'flow' is returned.
+ */
+int
+flow_count_mpls_labels(const struct flow *flow, struct flow_wildcards *wc)
+{
+ if (wc) {
+ wc->masks.dl_type = OVS_BE16_MAX;
+ }
+ if (eth_type_mpls(flow->dl_type)) {
+ int i;
+ int len = FLOW_MAX_MPLS_LABELS;
+
+ for (i = 0; i < len; i++) {
+ if (wc) {
+ wc->masks.mpls_lse[i] |= htonl(MPLS_BOS_MASK);
+ }
+ if (flow->mpls_lse[i] & htonl(MPLS_BOS_MASK)) {
+ return i + 1;
+ }
+ }
+
+ return len;
+ } else {
+ return 0;
+ }
+}
+
+/* Returns the number consecutive of MPLS LSEs, starting at the
+ * innermost LSE, that are common in 'a' and 'b'.
+ *
+ * 'an' must be flow_count_mpls_labels(a).
+ * 'bn' must be flow_count_mpls_labels(b).
+ */
+int
+flow_count_common_mpls_labels(const struct flow *a, int an,
+ const struct flow *b, int bn,
+ struct flow_wildcards *wc)
+{
+ int min_n = MIN(an, bn);
+ if (min_n == 0) {
+ return 0;
+ } else {
+ int common_n = 0;
+ int a_last = an - 1;
+ int b_last = bn - 1;
+ int i;
+
+ for (i = 0; i < min_n; i++) {
+ if (wc) {
+ wc->masks.mpls_lse[a_last - i] = OVS_BE32_MAX;
+ wc->masks.mpls_lse[b_last - i] = OVS_BE32_MAX;
+ }
+ if (a->mpls_lse[a_last - i] != b->mpls_lse[b_last - i]) {
+ break;
+ } else {
+ common_n++;
+ }
+ }
+
+ return common_n;
+ }
+}
+
+/* Adds a new outermost MPLS label to 'flow' and changes 'flow''s Ethernet type
+ * to 'mpls_eth_type', which must be an MPLS Ethertype.
+ *
+ * If the new label is the first MPLS label in 'flow', it is generated as;
+ *
+ * - label: 2, if 'flow' is IPv6, otherwise 0.
+ *
+ * - TTL: IPv4 or IPv6 TTL, if present and nonzero, otherwise 64.
+ *
+ * - TC: IPv4 or IPv6 TOS, if present, otherwise 0.
+ *
+ * - BoS: 1.
+ *
+ * If the new label is the second or label MPLS label in 'flow', it is
+ * generated as;
+ *
+ * - label: Copied from outer label.
+ *
+ * - TTL: Copied from outer label.
+ *
+ * - TC: Copied from outer label.
+ *
+ * - BoS: 0.
+ *
+ * 'n' must be flow_count_mpls_labels(flow). 'n' must be less than
+ * FLOW_MAX_MPLS_LABELS (because otherwise flow->mpls_lse[] would overflow).
+ */
+void
+flow_push_mpls(struct flow *flow, int n, ovs_be16 mpls_eth_type,
+ struct flow_wildcards *wc)
+{
+ ovs_assert(eth_type_mpls(mpls_eth_type));
+ ovs_assert(n < FLOW_MAX_MPLS_LABELS);
+
+ memset(wc->masks.mpls_lse, 0xff, sizeof wc->masks.mpls_lse);
+ if (n) {
+ int i;
+
+ for (i = n; i >= 1; i--) {
+ flow->mpls_lse[i] = flow->mpls_lse[i - 1];
+ }
+ flow->mpls_lse[0] = (flow->mpls_lse[1]
+ & htonl(~MPLS_BOS_MASK));
+ } else {
+ int label = 0; /* IPv4 Explicit Null. */
+ int tc = 0;
+ int ttl = 64;
+
+ if (flow->dl_type == htons(ETH_TYPE_IPV6)) {
+ label = 2;
+ }
+
+ if (is_ip_any(flow)) {
+ tc = (flow->nw_tos & IP_DSCP_MASK) >> 2;
+ wc->masks.nw_tos |= IP_DSCP_MASK;
+
+ if (flow->nw_ttl) {
+ ttl = flow->nw_ttl;
+ }
+ wc->masks.nw_ttl = 0xff;
+ }
+
+ 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);
+ memset((char *) flow + FLOW_SEGMENT_2_ENDS_AT, 0,
+ sizeof(struct flow) - FLOW_SEGMENT_2_ENDS_AT);
+ }
+ flow->dl_type = mpls_eth_type;
+}
+
+/* Tries to remove the outermost MPLS label from 'flow'. Returns true if
+ * successful, false otherwise. On success, sets 'flow''s Ethernet type to
+ * 'eth_type'.
+ *
+ * 'n' must be flow_count_mpls_labels(flow). */
+bool
+flow_pop_mpls(struct flow *flow, int n, ovs_be16 eth_type,
+ struct flow_wildcards *wc)
+{
+ int i;
+
+ if (n == 0) {
+ /* Nothing to pop. */
+ return false;
+ } else if (n == FLOW_MAX_MPLS_LABELS
+ && !(flow->mpls_lse[n - 1] & htonl(MPLS_BOS_MASK))) {
+ /* Can't pop because we don't know what to fill in mpls_lse[n - 1]. */
+ return false;
+ }
+
+ memset(wc->masks.mpls_lse, 0xff, sizeof wc->masks.mpls_lse);
+ for (i = 1; i < n; i++) {
+ flow->mpls_lse[i - 1] = flow->mpls_lse[i];
+ }
+ flow->mpls_lse[n - 1] = 0;
+ flow->dl_type = eth_type;
+ return true;
+}
+
+/* Sets the MPLS Label that 'flow' matches to 'label', which is interpreted
+ * as an OpenFlow 1.1 "mpls_label" value. */
+void
+flow_set_mpls_label(struct flow *flow, int idx, ovs_be32 label)
+{
+ set_mpls_lse_label(&flow->mpls_lse[idx], label);
+}
+
+/* Sets the MPLS TTL that 'flow' matches to 'ttl', which should be in the
+ * range 0...255. */
+void
+flow_set_mpls_ttl(struct flow *flow, int idx, uint8_t ttl)
+{
+ set_mpls_lse_ttl(&flow->mpls_lse[idx], ttl);
+}
+
+/* Sets the MPLS TC that 'flow' matches to 'tc', which should be in the
+ * range 0...7. */
+void
+flow_set_mpls_tc(struct flow *flow, int idx, uint8_t tc)
+{
+ set_mpls_lse_tc(&flow->mpls_lse[idx], tc);
+}
+
+/* Sets the MPLS BOS bit that 'flow' matches to which should be 0 or 1. */
+void
+flow_set_mpls_bos(struct flow *flow, int idx, uint8_t bos)
+{
+ set_mpls_lse_bos(&flow->mpls_lse[idx], bos);
+}
+
+/* Sets the entire MPLS LSE. */
+void
+flow_set_mpls_lse(struct flow *flow, int idx, ovs_be32 lse)
+{
+ flow->mpls_lse[idx] = lse;
+}
+
+static size_t
+flow_compose_l4(struct ofpbuf *b, const struct flow *flow)
+{
+ size_t l4_len = 0;
+
+ if (!(flow->nw_frag & FLOW_NW_FRAG_ANY)
+ || !(flow->nw_frag & FLOW_NW_FRAG_LATER)) {
+ if (flow->nw_proto == IPPROTO_TCP) {
+ struct tcp_header *tcp;
+
+ l4_len = sizeof *tcp;
+ tcp = ofpbuf_put_zeros(b, l4_len);
+ tcp->tcp_src = flow->tp_src;
+ tcp->tcp_dst = flow->tp_dst;
+ tcp->tcp_ctl = TCP_CTL(ntohs(flow->tcp_flags), 5);
+ } else if (flow->nw_proto == IPPROTO_UDP) {
+ struct udp_header *udp;
+
+ l4_len = sizeof *udp;
+ udp = ofpbuf_put_zeros(b, l4_len);
+ udp->udp_src = flow->tp_src;
+ udp->udp_dst = flow->tp_dst;
+ } else if (flow->nw_proto == IPPROTO_SCTP) {
+ struct sctp_header *sctp;
+
+ l4_len = sizeof *sctp;
+ sctp = ofpbuf_put_zeros(b, l4_len);
+ sctp->sctp_src = flow->tp_src;
+ sctp->sctp_dst = flow->tp_dst;
+ } else if (flow->nw_proto == IPPROTO_ICMP) {
+ struct icmp_header *icmp;
+
+ l4_len = sizeof *icmp;
+ icmp = ofpbuf_put_zeros(b, l4_len);
+ 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_ICMPV6) {
+ struct icmp6_hdr *icmp;
+
+ l4_len = sizeof *icmp;
+ icmp = ofpbuf_put_zeros(b, l4_len);
+ icmp->icmp6_type = ntohs(flow->tp_src);
+ icmp->icmp6_code = ntohs(flow->tp_dst);
+
+ if (icmp->icmp6_code == 0 &&
+ (icmp->icmp6_type == ND_NEIGHBOR_SOLICIT ||
+ icmp->icmp6_type == ND_NEIGHBOR_ADVERT)) {
+ struct in6_addr *nd_target;
+ struct nd_opt_hdr *nd_opt;
+
+ l4_len += sizeof *nd_target;
+ nd_target = ofpbuf_put_zeros(b, sizeof *nd_target);
+ *nd_target = flow->nd_target;
+
+ if (!eth_addr_is_zero(flow->arp_sha)) {
+ l4_len += 8;
+ nd_opt = ofpbuf_put_zeros(b, 8);
+ nd_opt->nd_opt_len = 1;
+ nd_opt->nd_opt_type = ND_OPT_SOURCE_LINKADDR;
+ memcpy(nd_opt + 1, flow->arp_sha, ETH_ADDR_LEN);
+ }
+ if (!eth_addr_is_zero(flow->arp_tha)) {
+ l4_len += 8;
+ nd_opt = ofpbuf_put_zeros(b, 8);
+ nd_opt->nd_opt_len = 1;
+ nd_opt->nd_opt_type = ND_OPT_TARGET_LINKADDR;
+ memcpy(nd_opt + 1, flow->arp_tha, ETH_ADDR_LEN);
+ }
+ }
+ icmp->icmp6_cksum = (OVS_FORCE uint16_t)
+ csum(icmp, (char *)ofpbuf_tail(b) - (char *)icmp);
+ }
+ }
+ return l4_len;
+}
+