Blob Blame History Raw
diff --git a/NEWS b/NEWS
index 75f26ddb7..4043ecf20 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,6 @@
+OVN v21.12.1 - xx xxx xxxx
+--------------------------
+
 OVN v21.12.0 - 22 Dec 2021
 --------------------------
   - Set ignore_lsp_down to true as default, so that ARP responder flows are
diff --git a/configure.ac b/configure.ac
index 48b4662f0..e44c86c74 100644
--- a/configure.ac
+++ b/configure.ac
@@ -13,7 +13,7 @@
 # limitations under the License.
 
 AC_PREREQ(2.63)
-AC_INIT(ovn, 21.12.0, bugs@openvswitch.org)
+AC_INIT(ovn, 21.12.1, bugs@openvswitch.org)
 AC_CONFIG_MACRO_DIR([m4])
 AC_CONFIG_AUX_DIR([build-aux])
 AC_CONFIG_HEADERS([config.h])
diff --git a/controller/physical.c b/controller/physical.c
index 836fc769a..aa651b876 100644
--- a/controller/physical.c
+++ b/controller/physical.c
@@ -1477,10 +1477,12 @@ consider_mc_group(struct ovsdb_idl_index *sbrec_port_binding_by_name,
             put_load(port->tunnel_key, MFF_LOG_OUTPORT, 0, 32,
                      &remote_ofpacts);
             put_resubmit(OFTABLE_CHECK_LOOPBACK, &remote_ofpacts);
-        } else if (local_binding_get_primary_pb(local_bindings, lport_name)
-                   || simap_contains(patch_ofports, port->logical_port)
-                   || (!strcmp(port->type, "l3gateway")
-                       && port->chassis == chassis)) {
+        } else if (port->chassis == chassis
+                   && (local_binding_get_primary_pb(local_bindings, lport_name)
+                       || !strcmp(port->type, "l3gateway"))) {
+            put_load(port->tunnel_key, MFF_LOG_OUTPORT, 0, 32, &ofpacts);
+            put_resubmit(OFTABLE_CHECK_LOOPBACK, &ofpacts);
+        } else if (simap_contains(patch_ofports, port->logical_port)) {
             put_load(port->tunnel_key, MFF_LOG_OUTPORT, 0, 32, &ofpacts);
             put_resubmit(OFTABLE_CHECK_LOOPBACK, &ofpacts);
         } else if (!strcmp(port->type, "chassisredirect")
diff --git a/controller/pinctrl.c b/controller/pinctrl.c
index f0667807e..d2bb7f441 100644
--- a/controller/pinctrl.c
+++ b/controller/pinctrl.c
@@ -1624,12 +1624,8 @@ pinctrl_handle_icmp(struct rconn *swconn, const struct flow *ip_flow,
     struct dp_packet packet;
 
     dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub);
-    dp_packet_clear(&packet);
-    packet.packet_type = htonl(PT_ETH);
-
-    struct eth_header *eh = dp_packet_put_zeros(&packet, sizeof *eh);
-    eh->eth_dst = loopback ? ip_flow->dl_src : ip_flow->dl_dst;
-    eh->eth_src = loopback ? ip_flow->dl_dst : ip_flow->dl_src;
+    struct eth_addr eth_src = loopback ? ip_flow->dl_dst : ip_flow->dl_src;
+    struct eth_addr eth_dst = loopback ? ip_flow->dl_src : ip_flow->dl_dst;
 
     if (get_dl_type(ip_flow) == htons(ETH_TYPE_IP)) {
         struct ip_header *in_ip = dp_packet_l3(pkt_in);
@@ -1642,27 +1638,8 @@ pinctrl_handle_icmp(struct rconn *swconn, const struct flow *ip_flow,
             return;
         }
 
-        struct ip_header *nh = dp_packet_put_zeros(&packet, sizeof *nh);
-
-        eh->eth_type = htons(ETH_TYPE_IP);
-        dp_packet_set_l3(&packet, nh);
-        nh->ip_ihl_ver = IP_IHL_VER(5, 4);
-        nh->ip_tot_len = htons(sizeof(struct ip_header) +
-                               sizeof(struct icmp_header));
-        nh->ip_proto = IPPROTO_ICMP;
-        nh->ip_frag_off = htons(IP_DF);
         ovs_be32 nw_src = loopback ? ip_flow->nw_dst : ip_flow->nw_src;
         ovs_be32 nw_dst = loopback ? ip_flow->nw_src : ip_flow->nw_dst;
-        packet_set_ipv4(&packet, nw_src, nw_dst, ip_flow->nw_tos, 255);
-
-        uint8_t icmp_code =  1;
-        if (set_icmp_code && in_ip->ip_proto == IPPROTO_UDP) {
-            icmp_code = 3;
-        }
-
-        struct icmp_header *ih = dp_packet_put_zeros(&packet, sizeof *ih);
-        dp_packet_set_l4(&packet, ih);
-        packet_set_icmp(&packet, ICMP4_DST_UNREACH, icmp_code);
 
         /* RFC 1122: 3.2.2	MUST send at least the IP header and 8 bytes
          * of header. MAY send more.
@@ -1671,51 +1648,40 @@ pinctrl_handle_icmp(struct rconn *swconn, const struct flow *ip_flow,
          * So, lets return as much as we can. */
 
         /* Calculate available room to include the original IP + data. */
-        nh = dp_packet_l3(&packet);
-        uint16_t room = 576 - (sizeof *eh + ntohs(nh->ip_tot_len));
+        uint16_t room = 576 - (sizeof(struct eth_header)
+                               + sizeof(struct ip_header)
+                               + sizeof(struct icmp_header));
         if (in_ip_len > room) {
             in_ip_len = room;
         }
-        dp_packet_put(&packet, in_ip, in_ip_len);
 
-        /* dp_packet_put may reallocate the buffer. Get the l3 and l4
-            * header pointers again. */
-        nh = dp_packet_l3(&packet);
-        ih = dp_packet_l4(&packet);
-        uint16_t ip_total_len = ntohs(nh->ip_tot_len) + in_ip_len;
-        nh->ip_tot_len = htons(ip_total_len);
+        uint16_t ip_total_len = sizeof(struct icmp_header) + in_ip_len;
+
+        pinctrl_compose_ipv4(&packet, eth_src, eth_dst, nw_src, nw_dst,
+                             IPPROTO_ICMP, 255, ip_total_len);
+
+        uint8_t icmp_code = 1;
+        if (set_icmp_code && in_ip->ip_proto == IPPROTO_UDP) {
+            icmp_code = 3;
+        }
+
+        struct icmp_header *ih = dp_packet_l4(&packet);
+        packet_set_icmp(&packet, ICMP4_DST_UNREACH, icmp_code);
+
+        /* Include original IP + data. */
+        void *data = ih + 1;
+        memcpy(data, in_ip, in_ip_len);
+
         ih->icmp_csum = 0;
         ih->icmp_csum = csum(ih, sizeof *ih + in_ip_len);
-        nh->ip_csum = 0;
-        nh->ip_csum = csum(nh, sizeof *nh);
-
     } else {
-        struct ip6_hdr *nh = dp_packet_put_zeros(&packet, sizeof *nh);
-        struct icmp6_data_header *ih;
-        uint32_t icmpv6_csum;
-        struct ip6_hdr *in_ip = dp_packet_l3(pkt_in);
-
-        eh->eth_type = htons(ETH_TYPE_IPV6);
-        dp_packet_set_l3(&packet, nh);
-        nh->ip6_vfc = 0x60;
-        nh->ip6_nxt = IPPROTO_ICMPV6;
-        nh->ip6_plen = htons(ICMP6_DATA_HEADER_LEN);
+        struct ovs_16aligned_ip6_hdr *in_ip = dp_packet_l3(pkt_in);
+        uint16_t in_ip_len = (uint16_t) sizeof *in_ip + ntohs(in_ip->ip6_plen);
+
         const struct in6_addr *ip6_src =
             loopback ? &ip_flow->ipv6_dst : &ip_flow->ipv6_src;
         const struct in6_addr *ip6_dst =
             loopback ? &ip_flow->ipv6_src : &ip_flow->ipv6_dst;
-        packet_set_ipv6(&packet, ip6_src, ip6_dst, ip_flow->nw_tos,
-                        ip_flow->ipv6_label, 255);
-
-        ih = dp_packet_put_zeros(&packet, sizeof *ih);
-        dp_packet_set_l4(&packet, ih);
-        ih->icmp6_base.icmp6_type = ICMP6_DST_UNREACH;
-        ih->icmp6_base.icmp6_code = 1;
-
-        if (set_icmp_code && in_ip->ip6_nxt == IPPROTO_UDP) {
-            ih->icmp6_base.icmp6_code = ICMP6_DST_UNREACH_NOPORT;
-        }
-        ih->icmp6_base.icmp6_cksum = 0;
 
         /* RFC 4443: 3.1.
          *
@@ -1730,20 +1696,33 @@ pinctrl_handle_icmp(struct rconn *swconn, const struct flow *ip_flow,
          * |                exceeding the minimum IPv6 MTU [IPv6]          |
          * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
          */
-
-        uint16_t room = 1280 - (sizeof *eh + sizeof *nh +
-                                ICMP6_DATA_HEADER_LEN);
-        uint16_t in_ip_len = (uint16_t) sizeof *in_ip + ntohs(in_ip->ip6_plen);
+        uint16_t room = 1280 - (sizeof(struct eth_header)
+                                + sizeof(struct ip6_hdr)
+                                + ICMP6_DATA_HEADER_LEN);
         if (in_ip_len > room) {
             in_ip_len = room;
         }
 
-        dp_packet_put(&packet, in_ip, in_ip_len);
-        nh = dp_packet_l3(&packet);
-        nh->ip6_plen = htons(ICMP6_DATA_HEADER_LEN + in_ip_len);
+        uint16_t ip_total_len =
+            sizeof(struct icmp6_data_header) + in_ip_len;
+        pinctrl_compose_ipv6(&packet, eth_src, eth_dst,
+                             CONST_CAST(struct in6_addr *, ip6_src),
+                             CONST_CAST(struct in6_addr *, ip6_dst),
+                             IPPROTO_ICMPV6, 255, ip_total_len);
 
-        icmpv6_csum = packet_csum_pseudoheader6(dp_packet_l3(&packet));
-        ih = dp_packet_l4(&packet);
+        struct icmp6_data_header *ih = dp_packet_l4(&packet);
+        ih->icmp6_base.icmp6_type = ICMP6_DST_UNREACH;
+        ih->icmp6_base.icmp6_code = 1;
+
+        if (set_icmp_code && in_ip->ip6_nxt == IPPROTO_UDP) {
+            ih->icmp6_base.icmp6_code = ICMP6_DST_UNREACH_NOPORT;
+        }
+        ih->icmp6_base.icmp6_cksum = 0;
+
+        void *data = ih + 1;
+        memcpy(data, in_ip, in_ip_len);
+        uint32_t icmpv6_csum =
+            packet_csum_pseudoheader6(dp_packet_l3(&packet));
         ih->icmp6_base.icmp6_cksum = csum_finish(
             csum_continue(icmpv6_csum, ih,
                           in_ip_len + ICMP6_DATA_HEADER_LEN));
@@ -1777,7 +1756,6 @@ pinctrl_handle_tcp_reset(struct rconn *swconn, const struct flow *ip_flow,
     struct dp_packet packet;
 
     dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub);
-    packet.packet_type = htonl(PT_ETH);
 
     struct eth_addr eth_src = loopback ? ip_flow->dl_dst : ip_flow->dl_src;
     struct eth_addr eth_dst = loopback ? ip_flow->dl_src : ip_flow->dl_dst;
@@ -1798,8 +1776,8 @@ pinctrl_handle_tcp_reset(struct rconn *swconn, const struct flow *ip_flow,
                              IPPROTO_TCP, 63, TCP_HEADER_LEN);
     }
 
-    struct tcp_header *th = dp_packet_put_zeros(&packet, sizeof *th);
-    dp_packet_set_l4(&packet, th);
+    struct tcp_header *th = dp_packet_l4(&packet);
+
     th->tcp_dst = ip_flow->tp_src;
     th->tcp_src = ip_flow->tp_dst;
 
@@ -1825,18 +1803,6 @@ pinctrl_handle_tcp_reset(struct rconn *swconn, const struct flow *ip_flow,
     dp_packet_uninit(&packet);
 }
 
-static void dp_packet_put_sctp_abort(struct dp_packet *packet,
-                                     bool reflect_tag)
-{
-    struct sctp_chunk_header abort = {
-        .sctp_chunk_type = SCTP_CHUNK_TYPE_ABORT,
-        .sctp_chunk_flags = reflect_tag ? SCTP_ABORT_CHUNK_FLAG_T : 0,
-        .sctp_chunk_len = htons(SCTP_CHUNK_HEADER_LEN),
-    };
-
-    dp_packet_put(packet, &abort, sizeof abort);
-}
-
 static void
 pinctrl_handle_sctp_abort(struct rconn *swconn, const struct flow *ip_flow,
                          struct dp_packet *pkt_in,
@@ -1870,7 +1836,7 @@ pinctrl_handle_sctp_abort(struct rconn *swconn, const struct flow *ip_flow,
         return;
     }
 
-    const struct sctp_init_chunk *sh_in_init = NULL;
+    const struct sctp_16aligned_init_chunk *sh_in_init = NULL;
     if (sh_in_chunk->sctp_chunk_type == SCTP_CHUNK_TYPE_INIT) {
         static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
         sh_in_init = dp_packet_at(pkt_in, pkt_in->l4_ofs +
@@ -1909,8 +1875,7 @@ pinctrl_handle_sctp_abort(struct rconn *swconn, const struct flow *ip_flow,
                                                SCTP_CHUNK_HEADER_LEN);
     }
 
-    struct sctp_header *sh = dp_packet_put_zeros(&packet, sizeof *sh);
-    dp_packet_set_l4(&packet, sh);
+    struct sctp_header *sh = dp_packet_l4(&packet);
     sh->sctp_dst = ip_flow->tp_src;
     sh->sctp_src = ip_flow->tp_dst;
     put_16aligned_be32(&sh->sctp_csum, 0);
@@ -1918,7 +1883,7 @@ pinctrl_handle_sctp_abort(struct rconn *swconn, const struct flow *ip_flow,
     bool tag_reflected;
     if (get_16aligned_be32(&sh_in->sctp_vtag) == 0 && sh_in_init) {
         /* See RFC 4960 Section 8.4, item 3. */
-        put_16aligned_be32(&sh->sctp_vtag, sh_in_init->initiate_tag);
+        sh->sctp_vtag = sh_in_init->initiate_tag;
         tag_reflected = false;
     } else {
         /* See RFC 4960 Section 8.4, item 8. */
@@ -1926,7 +1891,11 @@ pinctrl_handle_sctp_abort(struct rconn *swconn, const struct flow *ip_flow,
         tag_reflected = true;
     }
 
-    dp_packet_put_sctp_abort(&packet, tag_reflected);
+    struct sctp_chunk_header *ah =
+        ALIGNED_CAST(struct sctp_chunk_header *, sh + 1);
+    ah->sctp_chunk_type = SCTP_CHUNK_TYPE_ABORT;
+    ah->sctp_chunk_flags = tag_reflected ? SCTP_ABORT_CHUNK_FLAG_T : 0,
+    ah->sctp_chunk_len = htons(SCTP_CHUNK_HEADER_LEN),
 
     put_16aligned_be32(&sh->sctp_csum, crc32c((void *) sh,
                                               dp_packet_l4_size(&packet)));
@@ -2942,7 +2911,7 @@ pinctrl_handle_dns_lookup(
         goto exit;
     }
 
-    uint16_t query_type = ntohs(*ALIGNED_CAST(const ovs_be16 *, in_dns_data));
+    uint16_t query_type = ntohs(get_unaligned_be16((void *) in_dns_data));
     /* Supported query types - A, AAAA, ANY and PTR */
     if (!(query_type == DNS_QUERY_TYPE_A || query_type == DNS_QUERY_TYPE_AAAA
           || query_type == DNS_QUERY_TYPE_ANY
@@ -4564,16 +4533,15 @@ pinctrl_compose_ipv4(struct dp_packet *packet, struct eth_addr eth_src,
                      ovs_be32 ipv4_dst, uint8_t ip_proto, uint8_t ttl,
                      uint16_t ip_payload_len)
 {
-    dp_packet_clear(packet);
-    packet->packet_type = htonl(PT_ETH);
-
-    struct eth_header *eh = dp_packet_put_zeros(packet, sizeof *eh);
-    struct ip_header *nh = dp_packet_put_zeros(packet, sizeof *nh);
+    struct ip_header *nh;
+    size_t ip_tot_len = sizeof *nh + ip_payload_len;
 
-    eh->eth_dst = eth_dst;
-    eh->eth_src = eth_src;
-    eh->eth_type = htons(ETH_TYPE_IP);
-    dp_packet_set_l3(packet, nh);
+    /* Need to deal with odd-sized IP length while making sure that the
+     * packet is still aligned.  Reserve 2 bytes for potential VLAN tags.
+     */
+    dp_packet_reserve(packet,
+                      sizeof(struct eth_header) + ROUND_UP(ip_tot_len, 2) + 2);
+    nh = eth_compose(packet, eth_dst, eth_src, ETH_TYPE_IP, ip_tot_len);
     nh->ip_ihl_ver = IP_IHL_VER(5, 4);
     nh->ip_tot_len = htons(sizeof *nh + ip_payload_len);
     nh->ip_tos = IP_DSCP_CS6;
@@ -4584,6 +4552,7 @@ pinctrl_compose_ipv4(struct dp_packet *packet, struct eth_addr eth_src,
 
     nh->ip_csum = 0;
     nh->ip_csum = csum(nh, sizeof *nh);
+    dp_packet_set_l4(packet, nh + 1);
 }
 
 static void
@@ -4592,23 +4561,21 @@ pinctrl_compose_ipv6(struct dp_packet *packet, struct eth_addr eth_src,
                      struct in6_addr *ipv6_dst, uint8_t ip_proto, uint8_t ttl,
                      uint16_t ip_payload_len)
 {
-    dp_packet_clear(packet);
-    packet->packet_type = htonl(PT_ETH);
-
-    struct eth_header *eh = dp_packet_put_zeros(packet, sizeof *eh);
-    struct ip6_hdr *nh = dp_packet_put_zeros(packet, sizeof *nh);
-
-    eh->eth_dst = eth_dst;
-    eh->eth_src = eth_src;
-    eh->eth_type = htons(ETH_TYPE_IPV6);
-    dp_packet_set_l3(packet, nh);
-    dp_packet_set_l4(packet, nh + 1);
+    struct ip6_hdr *nh;
+    size_t ip_tot_len = sizeof *nh + ip_payload_len;
 
+    /* Need to deal with odd-sized IP length while making sure that the
+     * packet is still aligned.  Reserve 2 bytes for potential VLAN tags.
+     */
+    dp_packet_reserve(packet,
+                      sizeof(struct eth_header) + ROUND_UP(ip_tot_len, 2) + 2);
+    nh = eth_compose(packet, eth_dst, eth_src, ETH_TYPE_IPV6, ip_tot_len);
     nh->ip6_vfc = 0x60;
     nh->ip6_nxt = ip_proto;
     nh->ip6_plen = htons(ip_payload_len);
 
     packet_set_ipv6(packet, ipv6_src, ipv6_dst, 0, 0, ttl);
+    dp_packet_set_l4(packet, nh + 1);
 }
 
 /*
@@ -5400,10 +5367,6 @@ ip_mcast_querier_send_igmp(struct rconn *swconn, struct ip_mcast_snoop *ip_ms)
                          ip_ms->cfg.query_ipv4_dst,
                          IPPROTO_IGMP, 1, sizeof(struct igmpv3_query_header));
 
-    struct igmpv3_query_header *igh =
-        dp_packet_put_zeros(&packet, sizeof *igh);
-    dp_packet_set_l4(&packet, igh);
-
     /* IGMP query max-response in tenths of seconds. */
     uint8_t max_response = ip_ms->cfg.query_max_resp_s * 10;
     uint8_t qqic = max_response;
@@ -5449,15 +5412,10 @@ ip_mcast_querier_send_mld(struct rconn *swconn, struct ip_mcast_snoop *ip_ms)
                          IPPROTO_HOPOPTS, 1,
                          IPV6_EXT_HEADER_LEN + MLD_QUERY_HEADER_LEN);
 
-    struct ipv6_ext_header *ext_hdr =
-        dp_packet_put_zeros(&packet, IPV6_EXT_HEADER_LEN);
+    struct ipv6_ext_header *ext_hdr = dp_packet_l4(&packet);
     packet_set_ipv6_ext_header(ext_hdr, IPPROTO_ICMPV6, 0, mld_router_alert,
                                ARRAY_SIZE(mld_router_alert));
 
-    struct mld_header *mh =
-        dp_packet_put_zeros(&packet, MLD_QUERY_HEADER_LEN);
-    dp_packet_set_l4(&packet, mh);
-
     /* MLD query max-response in milliseconds. */
     uint16_t max_response = ip_ms->cfg.query_max_resp_s * 1000;
     uint8_t qqic = ip_ms->cfg.query_max_resp_s;
@@ -6033,6 +5991,8 @@ pinctrl_handle_put_nd_ra_opts(
     struct dp_packet pkt_out;
     dp_packet_init(&pkt_out, new_packet_size);
     dp_packet_clear(&pkt_out);
+    /* Properly align after the ethernet header */
+    dp_packet_reserve(&pkt_out, 2);
     dp_packet_prealloc_tailroom(&pkt_out, new_packet_size);
     pkt_out_ptr = &pkt_out;
 
@@ -6155,23 +6115,26 @@ wait_controller_event(struct ovsdb_idl_txn *ovnsb_idl_txn)
 static bool
 pinctrl_handle_empty_lb_backends_opts(struct ofpbuf *userdata)
 {
-    struct controller_event_opt_header *userdata_opt;
+    struct controller_event_opt_header opt_hdr;
+    void *userdata_opt;
     uint32_t hash = 0;
     char *vip = NULL;
     char *protocol = NULL;
     char *load_balancer = NULL;
 
     while (userdata->size) {
-        userdata_opt = ofpbuf_try_pull(userdata, sizeof *userdata_opt);
+        userdata_opt = ofpbuf_try_pull(userdata, sizeof opt_hdr);
         if (!userdata_opt) {
             return false;
         }
-        size_t size = ntohs(userdata_opt->size);
+        memcpy(&opt_hdr, userdata_opt, sizeof opt_hdr);
+
+        size_t size = ntohs(opt_hdr.size);
         char *userdata_opt_data = ofpbuf_try_pull(userdata, size);
         if (!userdata_opt_data) {
             return false;
         }
-        switch (ntohs(userdata_opt->opt_code)) {
+        switch (ntohs(opt_hdr.opt_code)) {
         case EMPTY_LB_VIP:
             vip = xmemdup0(userdata_opt_data, size);
             break;
@@ -6820,8 +6783,6 @@ bfd_monitor_put_bfd_msg(struct bfd_entry *entry, struct dp_packet *packet,
 {
     int payload_len = sizeof(struct udp_header) + sizeof(struct bfd_msg);
 
-    /* Properly align after the ethernet header */
-    dp_packet_reserve(packet, 2);
     if (IN6_IS_ADDR_V4MAPPED(&entry->ip_src)) {
         ovs_be32 ip_src = in6_addr_get_mapped_ipv4(&entry->ip_src);
         ovs_be32 ip_dst = in6_addr_get_mapped_ipv4(&entry->ip_dst);
@@ -6833,13 +6794,13 @@ bfd_monitor_put_bfd_msg(struct bfd_entry *entry, struct dp_packet *packet,
                              MAXTTL, payload_len);
     }
 
-    struct udp_header *udp = dp_packet_put_zeros(packet, sizeof *udp);
+    struct udp_header *udp = dp_packet_l4(packet);
     udp->udp_len = htons(payload_len);
     udp->udp_csum = 0;
     udp->udp_src = htons(entry->udp_src);
     udp->udp_dst = htons(BFD_DEST_PORT);
 
-    struct bfd_msg *msg = dp_packet_put_zeros(packet, sizeof *msg);
+    struct bfd_msg *msg = ALIGNED_CAST(struct bfd_msg *, udp + 1);
     msg->vers_diag = (BFD_VERSION << 5);
     msg->mult = entry->local_mult;
     msg->length = BFD_PACKET_LEN;
@@ -7383,7 +7344,7 @@ svc_monitor_send_tcp_health_check__(struct rconn *swconn,
                          ip4_src, in6_addr_get_mapped_ipv4(&svc_mon->ip),
                          IPPROTO_TCP, 63, TCP_HEADER_LEN);
 
-    struct tcp_header *th = dp_packet_put_zeros(&packet, sizeof *th);
+    struct tcp_header *th = dp_packet_l4(&packet);
     dp_packet_set_l4(&packet, th);
     th->tcp_dst = htons(svc_mon->proto_port);
     th->tcp_src = tcp_src;
@@ -7446,13 +7407,12 @@ svc_monitor_send_udp_health_check(struct rconn *swconn,
                          ip4_src, in6_addr_get_mapped_ipv4(&svc_mon->ip),
                          IPPROTO_UDP, 63, UDP_HEADER_LEN + 8);
 
-    struct udp_header *uh = dp_packet_put_zeros(&packet, sizeof *uh);
+    struct udp_header *uh = dp_packet_l4(&packet);
     dp_packet_set_l4(&packet, uh);
     uh->udp_dst = htons(svc_mon->proto_port);
     uh->udp_src = udp_src;
     uh->udp_len = htons(UDP_HEADER_LEN + 8);
     uh->udp_csum = 0;
-    dp_packet_put_zeros(&packet, 8);
 
     uint64_t ofpacts_stub[4096 / 8];
     struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub);
diff --git a/debian/changelog b/debian/changelog
index 0cc5f14ac..0d8593083 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+OVN (21.12.1-1) unstable; urgency=low
+   [ OVN team ]
+   * New upstream version
+
+ -- OVN team <dev@openvswitch.org>  Wed, 22 Dec 2021 09:45:52 -0500
+
 ovn (21.12.0-1) unstable; urgency=low
 
    * New upstream version
diff --git a/lib/actions.c b/lib/actions.c
index da00ee349..a78d01198 100644
--- a/lib/actions.c
+++ b/lib/actions.c
@@ -1842,19 +1842,20 @@ encode_event_empty_lb_backends_opts(struct ofpbuf *ofpacts,
 {
     for (const struct ovnact_gen_option *o = event->options;
          o < &event->options[event->n_options]; o++) {
-        struct controller_event_opt_header *hdr =
-            ofpbuf_put_uninit(ofpacts, sizeof *hdr);
+
+        /* All empty_lb_backends fields are of type 'str' */
+        ovs_assert(!strcmp(o->option->type, "str"));
+
         const union expr_constant *c = o->value.values;
-        size_t size;
-        hdr->opt_code = htons(o->option->code);
-        if (!strcmp(o->option->type, "str")) {
-            size = strlen(c->string);
-            hdr->size = htons(size);
-            ofpbuf_put(ofpacts, c->string, size);
-        } else {
-            /* All empty_lb_backends fields are of type 'str' */
-            OVS_NOT_REACHED();
-        }
+        size_t size = strlen(c->string);
+        struct controller_event_opt_header hdr =
+            (struct controller_event_opt_header) {
+            .opt_code = htons(o->option->code),
+            .size = htons(size),
+        };
+
+        memcpy(ofpbuf_put_uninit(ofpacts, sizeof hdr), &hdr, sizeof hdr);
+        ofpbuf_put(ofpacts, c->string, size);
     }
 }
 
diff --git a/lib/ovn-l7.h b/lib/ovn-l7.h
index 9a33f5cda..256e963d9 100644
--- a/lib/ovn-l7.h
+++ b/lib/ovn-l7.h
@@ -502,7 +502,7 @@ struct mld_query_header {
     ovs_be16 csum;
     ovs_be16 max_resp;
     ovs_be16 rsvd;
-    struct in6_addr group;
+    union ovs_16aligned_in6_addr group;
     uint8_t srs_qrv;
     uint8_t qqic;
     ovs_be16 nsrcs;
@@ -518,7 +518,9 @@ packet_set_mld_query(struct dp_packet *packet, uint16_t max_resp,
                      const struct in6_addr *group,
                      bool srs, uint8_t qrv, uint8_t qqic)
 {
-    struct mld_query_header *mqh = dp_packet_l4(packet);
+    struct ipv6_ext_header *ext_hdr = dp_packet_l4(packet);
+    struct mld_query_header *mqh = ALIGNED_CAST(struct mld_query_header *,
+                                                ext_hdr + 1);
     mqh->type = MLD_QUERY;
     mqh->code = 0;
     mqh->max_resp = htons(max_resp);
diff --git a/lib/ovn-util.h b/lib/ovn-util.h
index a923c3b65..b212c64b7 100644
--- a/lib/ovn-util.h
+++ b/lib/ovn-util.h
@@ -261,14 +261,16 @@ struct sctp_chunk_header {
 BUILD_ASSERT_DECL(SCTP_CHUNK_HEADER_LEN == sizeof(struct sctp_chunk_header));
 
 #define SCTP_INIT_CHUNK_LEN 16
-struct sctp_init_chunk {
-    ovs_be32 initiate_tag;
-    ovs_be32 a_rwnd;
+struct sctp_16aligned_init_chunk {
+    ovs_16aligned_be32 initiate_tag;
+    ovs_16aligned_be32 a_rwnd;
     ovs_be16 num_outbound_streams;
     ovs_be16 num_inbound_streams;
-    ovs_be32 initial_tsn;
+    ovs_16aligned_be32 initial_tsn;
 };
-BUILD_ASSERT_DECL(SCTP_INIT_CHUNK_LEN == sizeof(struct sctp_init_chunk));
+BUILD_ASSERT_DECL(
+    SCTP_INIT_CHUNK_LEN == sizeof(struct sctp_16aligned_init_chunk)
+);
 
 /* These are the only SCTP chunk types that OVN cares about.
  * There is no need to define the other chunk types until they are
diff --git a/tests/ovn.at b/tests/ovn.at
index 9ec62e321..92e284e8a 100644
--- a/tests/ovn.at
+++ b/tests/ovn.at
@@ -9658,8 +9658,8 @@ as hv1 ovs-appctl netdev-dummy/receive vm1 $packet
 
 # expected packet at foo2
 packet=${dst_mac}${src_mac}8100000208004500001c0000000040110000${src_ip}${dst_ip}0035111100080000
-echo  $packet > expected
-OVN_CHECK_PACKETS([hv2/vm2-tx.pcap], [expected])
+echo  $packet > expected2
+OVN_CHECK_PACKETS([hv2/vm2-tx.pcap], [expected2])
 
 # Send ip packets between foo1 and bar2 (different switch, different HV)
 src_mac="f00000010205"
@@ -9673,8 +9673,8 @@ as hv1 ovs-appctl netdev-dummy/receive vm1 $packet
 src_mac="000000010204"
 dst_mac="f00000010208"
 packet=${dst_mac}${src_mac}8100000108004500001c000000003f110100${src_ip}${dst_ip}0035111100080000
-echo  $packet >> expected
-OVN_CHECK_PACKETS([hv2/vm2-tx.pcap], [expected])
+echo  $packet >> expected2
+OVN_CHECK_PACKETS([hv2/vm2-tx.pcap], [expected2])
 
 # Send ip packets between foo1 and bar1
 # (different switch, loopback to same vm but different tag)
@@ -9703,11 +9703,11 @@ as hv1 ovs-appctl netdev-dummy/receive vm1 $packet
 
 # expected packet at bar3
 packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000
-echo  $packet > expected
-OVN_CHECK_PACKETS([hv1/bar3-tx.pcap], [expected])
+echo  $packet > expected3
+OVN_CHECK_PACKETS([hv1/bar3-tx.pcap], [expected3])
 
 # Send ip packets between foo1 and vm1.
-(different switch, container to the VM hosting it.)
+# (different switch, container to the VM hosting it.)
 src_mac="f00000010205"
 dst_mac="000000010203"
 src_ip=`ip_to_hex 192 168 1 2`
@@ -9723,7 +9723,7 @@ echo  $packet >> expected1
 OVN_CHECK_PACKETS([hv1/vm1-tx.pcap], [expected1])
 
 # Send packets from vm1 to bar1.
-(different switch, A hosting VM to a container inside it)
+# (different switch, A hosting VM to a container inside it)
 src_mac="f00000010203"
 dst_mac="000000010202"
 src_ip=`ip_to_hex 172 16 1 2`
@@ -9739,6 +9739,7 @@ echo  $packet >> expected1
 OVN_CHECK_PACKETS([hv1/vm1-tx.pcap], [expected1])
 
 # Send broadcast packet from foo1. foo1 should not receive the same packet.
+# But foo2 should.
 src_mac="f00000010205"
 dst_mac="ffffffffffff"
 src_ip=`ip_to_hex 192 168 1 2`
@@ -9749,6 +9750,11 @@ as hv1 ovs-appctl netdev-dummy/receive vm1 $packet
 # expected packet at VM1
 OVN_CHECK_PACKETS([hv1/vm1-tx.pcap], [expected1])
 
+# expected packet at foo2
+packet=${dst_mac}${src_mac}8100000208004500001c0000000040110000${src_ip}${dst_ip}0035111100080000
+echo  $packet >> expected2
+OVN_CHECK_PACKETS([hv2/vm2-tx.pcap], [expected2])
+
 # Test binding of parent and container ports.
 ovn-nbctl lsp-set-options vm1 requested-chassis=foo