+ remaining = packet->l4_ofs - packet->l3_ofs;
+
+ if (remaining < sizeof *nh) {
+ return false;
+ }
+ nh = ALIGNED_CAST(struct ovs_16aligned_ip6_hdr *, data);
+ data += sizeof *nh;
+ remaining -= sizeof *nh;
+ nexthdr = nh->ip6_nxt;
+
+ while (1) {
+ if ((nexthdr != IPPROTO_HOPOPTS)
+ && (nexthdr != IPPROTO_ROUTING)
+ && (nexthdr != IPPROTO_DSTOPTS)
+ && (nexthdr != IPPROTO_AH)
+ && (nexthdr != IPPROTO_FRAGMENT)) {
+ /* It's either a terminal header (e.g., TCP, UDP) or one we
+ * don't understand. In either case, we're done with the
+ * packet, so use it to fill in 'nw_proto'. */
+ break;
+ }
+
+ /* We only verify that at least 8 bytes of the next header are
+ * available, but many of these headers are longer. Ensure that
+ * accesses within the extension header are within those first 8
+ * bytes. All extension headers are required to be at least 8
+ * bytes. */
+ if (remaining < 8) {
+ return false;
+ }
+
+ if (nexthdr == IPPROTO_AH) {
+ /* A standard AH definition isn't available, but the fields
+ * we care about are in the same location as the generic
+ * option header--only the header length is calculated
+ * differently. */
+ const struct ip6_ext *ext_hdr = (struct ip6_ext *)data;
+
+ nexthdr = ext_hdr->ip6e_nxt;
+ len = (ext_hdr->ip6e_len + 2) * 4;
+ } else if (nexthdr == IPPROTO_FRAGMENT) {
+ const struct ovs_16aligned_ip6_frag *frag_hdr
+ = ALIGNED_CAST(struct ovs_16aligned_ip6_frag *, data);
+
+ nexthdr = frag_hdr->ip6f_nxt;
+ len = sizeof *frag_hdr;
+ } else if (nexthdr == IPPROTO_ROUTING) {
+ const struct ip6_rthdr *rh = (struct ip6_rthdr *)data;
+
+ if (rh->ip6r_segleft > 0) {
+ return true;
+ }
+
+ nexthdr = rh->ip6r_nxt;
+ len = (rh->ip6r_len + 1) * 8;
+ } else {
+ const struct ip6_ext *ext_hdr = (struct ip6_ext *)data;
+
+ nexthdr = ext_hdr->ip6e_nxt;
+ len = (ext_hdr->ip6e_len + 1) * 8;
+ }
+
+ if (remaining < len) {
+ return false;
+ }
+ remaining -= len;
+ data += len;
+ }
+
+ return false;
+}
+
+static void
+packet_update_csum128(struct ofpbuf *packet, uint8_t proto,
+ ovs_16aligned_be32 addr[4], const ovs_be32 new_addr[4])
+{
+ size_t l4_size = ofpbuf_l4_size(packet);
+
+ if (proto == IPPROTO_TCP && l4_size >= TCP_HEADER_LEN) {
+ struct tcp_header *th = ofpbuf_l4(packet);
+
+ th->tcp_csum = recalc_csum128(th->tcp_csum, addr, new_addr);
+ } else if (proto == IPPROTO_UDP && l4_size >= UDP_HEADER_LEN) {
+ struct udp_header *uh = ofpbuf_l4(packet);
+
+ if (uh->udp_csum) {
+ uh->udp_csum = recalc_csum128(uh->udp_csum, addr, new_addr);
+ if (!uh->udp_csum) {
+ uh->udp_csum = htons(0xffff);
+ }
+ }
+ }
+}
+
+static void
+packet_set_ipv6_addr(struct ofpbuf *packet, uint8_t proto,
+ ovs_16aligned_be32 addr[4], const ovs_be32 new_addr[4],
+ bool recalculate_csum)
+{
+ if (recalculate_csum) {
+ packet_update_csum128(packet, proto, addr, new_addr);
+ }
+ memcpy(addr, new_addr, sizeof(ovs_be32[4]));
+}
+
+static void
+packet_set_ipv6_flow_label(ovs_16aligned_be32 *flow_label, ovs_be32 flow_key)
+{
+ ovs_be32 old_label = get_16aligned_be32(flow_label);
+ ovs_be32 new_label = (old_label & htonl(~IPV6_LABEL_MASK)) | flow_key;
+ put_16aligned_be32(flow_label, new_label);
+}
+
+static void
+packet_set_ipv6_tc(ovs_16aligned_be32 *flow_label, uint8_t tc)
+{
+ ovs_be32 old_label = get_16aligned_be32(flow_label);
+ ovs_be32 new_label = (old_label & htonl(0xF00FFFFF)) | htonl(tc << 20);
+ put_16aligned_be32(flow_label, new_label);
+}
+
+/* Modifies the IPv4 header fields of 'packet' to be consistent with 'src',
+ * 'dst', 'tos', and 'ttl'. Updates 'packet''s L4 checksums as appropriate.
+ * 'packet' must contain a valid IPv4 packet with correctly populated l[347]
+ * markers. */
+void
+packet_set_ipv4(struct ofpbuf *packet, ovs_be32 src, ovs_be32 dst,
+ uint8_t tos, uint8_t ttl)
+{
+ struct ip_header *nh = ofpbuf_l3(packet);
+
+ if (get_16aligned_be32(&nh->ip_src) != src) {
+ packet_set_ipv4_addr(packet, &nh->ip_src, src);
+ }
+
+ if (get_16aligned_be32(&nh->ip_dst) != dst) {
+ packet_set_ipv4_addr(packet, &nh->ip_dst, dst);
+ }
+
+ if (nh->ip_tos != tos) {
+ uint8_t *field = &nh->ip_tos;
+
+ nh->ip_csum = recalc_csum16(nh->ip_csum, htons((uint16_t) *field),
+ htons((uint16_t) tos));
+ *field = tos;
+ }
+
+ if (nh->ip_ttl != ttl) {
+ uint8_t *field = &nh->ip_ttl;
+
+ nh->ip_csum = recalc_csum16(nh->ip_csum, htons(*field << 8),
+ htons(ttl << 8));
+ *field = ttl;
+ }
+}
+
+/* Modifies the IPv6 header fields of 'packet' to be consistent with 'src',
+ * 'dst', 'traffic class', and 'next hop'. Updates 'packet''s L4 checksums as
+ * appropriate. 'packet' must contain a valid IPv6 packet with correctly
+ * populated l[34] offsets. */
+void
+packet_set_ipv6(struct ofpbuf *packet, uint8_t proto, const ovs_be32 src[4],
+ const ovs_be32 dst[4], uint8_t key_tc, ovs_be32 key_fl,
+ uint8_t key_hl)
+{
+ struct ovs_16aligned_ip6_hdr *nh = ofpbuf_l3(packet);
+
+ if (memcmp(&nh->ip6_src, src, sizeof(ovs_be32[4]))) {
+ packet_set_ipv6_addr(packet, proto, nh->ip6_src.be32, src, true);
+ }
+
+ if (memcmp(&nh->ip6_dst, dst, sizeof(ovs_be32[4]))) {
+ packet_set_ipv6_addr(packet, proto, nh->ip6_dst.be32, dst,
+ !packet_rh_present(packet));
+ }
+
+ packet_set_ipv6_tc(&nh->ip6_flow, key_tc);
+
+ packet_set_ipv6_flow_label(&nh->ip6_flow, key_fl);
+
+ nh->ip6_hlim = key_hl;
+}
+
+static void
+packet_set_port(ovs_be16 *port, ovs_be16 new_port, ovs_be16 *csum)
+{
+ if (*port != new_port) {
+ *csum = recalc_csum16(*csum, *port, new_port);
+ *port = new_port;
+ }
+}
+
+/* Sets the TCP source and destination port ('src' and 'dst' respectively) of
+ * the TCP header contained in 'packet'. 'packet' must be a valid TCP packet
+ * with its l4 offset properly populated. */
+void
+packet_set_tcp_port(struct ofpbuf *packet, ovs_be16 src, ovs_be16 dst)
+{
+ struct tcp_header *th = ofpbuf_l4(packet);
+
+ packet_set_port(&th->tcp_src, src, &th->tcp_csum);
+ packet_set_port(&th->tcp_dst, dst, &th->tcp_csum);
+}
+
+/* Sets the UDP source and destination port ('src' and 'dst' respectively) of
+ * the UDP header contained in 'packet'. 'packet' must be a valid UDP packet
+ * with its l4 offset properly populated. */
+void
+packet_set_udp_port(struct ofpbuf *packet, ovs_be16 src, ovs_be16 dst)
+{
+ struct udp_header *uh = ofpbuf_l4(packet);
+
+ if (uh->udp_csum) {
+ packet_set_port(&uh->udp_src, src, &uh->udp_csum);
+ packet_set_port(&uh->udp_dst, dst, &uh->udp_csum);
+
+ if (!uh->udp_csum) {
+ uh->udp_csum = htons(0xffff);
+ }
+ } else {
+ uh->udp_src = src;
+ uh->udp_dst = dst;
+ }
+}
+
+/* Sets the SCTP source and destination port ('src' and 'dst' respectively) of
+ * the SCTP header contained in 'packet'. 'packet' must be a valid SCTP packet
+ * with its l4 offset properly populated. */
+void
+packet_set_sctp_port(struct ofpbuf *packet, ovs_be16 src, ovs_be16 dst)
+{
+ struct sctp_header *sh = ofpbuf_l4(packet);
+ ovs_be32 old_csum, old_correct_csum, new_csum;
+ uint16_t tp_len = ofpbuf_l4_size(packet);
+
+ old_csum = get_16aligned_be32(&sh->sctp_csum);
+ put_16aligned_be32(&sh->sctp_csum, 0);
+ old_correct_csum = crc32c((void *)sh, tp_len);
+
+ sh->sctp_src = src;
+ sh->sctp_dst = dst;
+
+ new_csum = crc32c((void *)sh, tp_len);
+ put_16aligned_be32(&sh->sctp_csum, old_csum ^ old_correct_csum ^ new_csum);
+}
+
+const char *
+packet_tcp_flag_to_string(uint32_t flag)
+{
+ switch (flag) {
+ case TCP_FIN:
+ return "fin";
+ case TCP_SYN:
+ return "syn";
+ case TCP_RST:
+ return "rst";
+ case TCP_PSH:
+ return "psh";
+ case TCP_ACK:
+ return "ack";
+ case TCP_URG:
+ return "urg";
+ case TCP_ECE:
+ return "ece";
+ case TCP_CWR:
+ return "cwr";
+ case TCP_NS:
+ return "ns";
+ case 0x200:
+ return "[200]";
+ case 0x400:
+ return "[400]";
+ case 0x800:
+ return "[800]";
+ default:
+ return NULL;
+ }
+}
+
+/* Appends a string representation of the TCP flags value 'tcp_flags'
+ * (e.g. from struct flow.tcp_flags or obtained via TCP_FLAGS) to 's', in the
+ * format used by tcpdump. */
+void
+packet_format_tcp_flags(struct ds *s, uint16_t tcp_flags)
+{
+ if (!tcp_flags) {
+ ds_put_cstr(s, "none");
+ return;
+ }
+
+ if (tcp_flags & TCP_SYN) {
+ ds_put_char(s, 'S');
+ }
+ if (tcp_flags & TCP_FIN) {
+ ds_put_char(s, 'F');
+ }
+ if (tcp_flags & TCP_PSH) {
+ ds_put_char(s, 'P');
+ }
+ if (tcp_flags & TCP_RST) {
+ ds_put_char(s, 'R');
+ }
+ if (tcp_flags & TCP_URG) {
+ ds_put_char(s, 'U');
+ }
+ if (tcp_flags & TCP_ACK) {
+ ds_put_char(s, '.');
+ }
+ if (tcp_flags & TCP_ECE) {
+ ds_put_cstr(s, "E");
+ }
+ if (tcp_flags & TCP_CWR) {
+ ds_put_cstr(s, "C");
+ }
+ if (tcp_flags & TCP_NS) {
+ ds_put_cstr(s, "N");
+ }
+ if (tcp_flags & 0x200) {
+ ds_put_cstr(s, "[200]");
+ }
+ if (tcp_flags & 0x400) {
+ ds_put_cstr(s, "[400]");
+ }
+ if (tcp_flags & 0x800) {
+ ds_put_cstr(s, "[800]");
+ }