| From 3e8b6e8e69d1cdf10522e68bbfb3a358a0eab21d Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Fri, 18 Mar 2022 08:43:19 -0400 |
| Subject: openvswitch: always update flow key after nat |
| |
| From: Aaron Conole <aconole@redhat.com> |
| |
| [ Upstream commit 60b44ca6bd7518dd38fa2719bc9240378b6172c3 ] |
| |
| During NAT, a tuple collision may occur. When this happens, openvswitch |
| will make a second pass through NAT which will perform additional packet |
| modification. This will update the skb data, but not the flow key that |
| OVS uses. This means that future flow lookups, and packet matches will |
| have incorrect data. This has been supported since |
| 5d50aa83e2c8 ("openvswitch: support asymmetric conntrack"). |
| |
| That commit failed to properly update the sw_flow_key attributes, since |
| it only called the ovs_ct_nat_update_key once, rather than each time |
| ovs_ct_nat_execute was called. As these two operations are linked, the |
| ovs_ct_nat_execute() function should always make sure that the |
| sw_flow_key is updated after a successful call through NAT infrastructure. |
| |
| Fixes: 5d50aa83e2c8 ("openvswitch: support asymmetric conntrack") |
| Cc: Dumitru Ceara <dceara@redhat.com> |
| Cc: Numan Siddique <nusiddiq@redhat.com> |
| Signed-off-by: Aaron Conole <aconole@redhat.com> |
| Acked-by: Eelco Chaudron <echaudro@redhat.com> |
| Link: https://lore.kernel.org/r/20220318124319.3056455-1-aconole@redhat.com |
| Signed-off-by: Jakub Kicinski <kuba@kernel.org> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| net/openvswitch/conntrack.c | 118 ++++++++++++++++++------------------ |
| 1 file changed, 59 insertions(+), 59 deletions(-) |
| |
| diff --git a/net/openvswitch/conntrack.c b/net/openvswitch/conntrack.c |
| index b6f98eba71f1..816036b9c223 100644 |
| --- a/net/openvswitch/conntrack.c |
| +++ b/net/openvswitch/conntrack.c |
| @@ -730,6 +730,57 @@ static bool skb_nfct_cached(struct net *net, |
| } |
| |
| #if IS_ENABLED(CONFIG_NF_NAT) |
| +static void ovs_nat_update_key(struct sw_flow_key *key, |
| + const struct sk_buff *skb, |
| + enum nf_nat_manip_type maniptype) |
| +{ |
| + if (maniptype == NF_NAT_MANIP_SRC) { |
| + __be16 src; |
| + |
| + key->ct_state |= OVS_CS_F_SRC_NAT; |
| + if (key->eth.type == htons(ETH_P_IP)) |
| + key->ipv4.addr.src = ip_hdr(skb)->saddr; |
| + else if (key->eth.type == htons(ETH_P_IPV6)) |
| + memcpy(&key->ipv6.addr.src, &ipv6_hdr(skb)->saddr, |
| + sizeof(key->ipv6.addr.src)); |
| + else |
| + return; |
| + |
| + if (key->ip.proto == IPPROTO_UDP) |
| + src = udp_hdr(skb)->source; |
| + else if (key->ip.proto == IPPROTO_TCP) |
| + src = tcp_hdr(skb)->source; |
| + else if (key->ip.proto == IPPROTO_SCTP) |
| + src = sctp_hdr(skb)->source; |
| + else |
| + return; |
| + |
| + key->tp.src = src; |
| + } else { |
| + __be16 dst; |
| + |
| + key->ct_state |= OVS_CS_F_DST_NAT; |
| + if (key->eth.type == htons(ETH_P_IP)) |
| + key->ipv4.addr.dst = ip_hdr(skb)->daddr; |
| + else if (key->eth.type == htons(ETH_P_IPV6)) |
| + memcpy(&key->ipv6.addr.dst, &ipv6_hdr(skb)->daddr, |
| + sizeof(key->ipv6.addr.dst)); |
| + else |
| + return; |
| + |
| + if (key->ip.proto == IPPROTO_UDP) |
| + dst = udp_hdr(skb)->dest; |
| + else if (key->ip.proto == IPPROTO_TCP) |
| + dst = tcp_hdr(skb)->dest; |
| + else if (key->ip.proto == IPPROTO_SCTP) |
| + dst = sctp_hdr(skb)->dest; |
| + else |
| + return; |
| + |
| + key->tp.dst = dst; |
| + } |
| +} |
| + |
| /* Modelled after nf_nat_ipv[46]_fn(). |
| * range is only used for new, uninitialized NAT state. |
| * Returns either NF_ACCEPT or NF_DROP. |
| @@ -737,7 +788,7 @@ static bool skb_nfct_cached(struct net *net, |
| static int ovs_ct_nat_execute(struct sk_buff *skb, struct nf_conn *ct, |
| enum ip_conntrack_info ctinfo, |
| const struct nf_nat_range2 *range, |
| - enum nf_nat_manip_type maniptype) |
| + enum nf_nat_manip_type maniptype, struct sw_flow_key *key) |
| { |
| int hooknum, nh_off, err = NF_ACCEPT; |
| |
| @@ -810,58 +861,11 @@ static int ovs_ct_nat_execute(struct sk_buff *skb, struct nf_conn *ct, |
| skb_push(skb, nh_off); |
| skb_postpush_rcsum(skb, skb->data, nh_off); |
| |
| - return err; |
| -} |
| - |
| -static void ovs_nat_update_key(struct sw_flow_key *key, |
| - const struct sk_buff *skb, |
| - enum nf_nat_manip_type maniptype) |
| -{ |
| - if (maniptype == NF_NAT_MANIP_SRC) { |
| - __be16 src; |
| - |
| - key->ct_state |= OVS_CS_F_SRC_NAT; |
| - if (key->eth.type == htons(ETH_P_IP)) |
| - key->ipv4.addr.src = ip_hdr(skb)->saddr; |
| - else if (key->eth.type == htons(ETH_P_IPV6)) |
| - memcpy(&key->ipv6.addr.src, &ipv6_hdr(skb)->saddr, |
| - sizeof(key->ipv6.addr.src)); |
| - else |
| - return; |
| - |
| - if (key->ip.proto == IPPROTO_UDP) |
| - src = udp_hdr(skb)->source; |
| - else if (key->ip.proto == IPPROTO_TCP) |
| - src = tcp_hdr(skb)->source; |
| - else if (key->ip.proto == IPPROTO_SCTP) |
| - src = sctp_hdr(skb)->source; |
| - else |
| - return; |
| - |
| - key->tp.src = src; |
| - } else { |
| - __be16 dst; |
| - |
| - key->ct_state |= OVS_CS_F_DST_NAT; |
| - if (key->eth.type == htons(ETH_P_IP)) |
| - key->ipv4.addr.dst = ip_hdr(skb)->daddr; |
| - else if (key->eth.type == htons(ETH_P_IPV6)) |
| - memcpy(&key->ipv6.addr.dst, &ipv6_hdr(skb)->daddr, |
| - sizeof(key->ipv6.addr.dst)); |
| - else |
| - return; |
| - |
| - if (key->ip.proto == IPPROTO_UDP) |
| - dst = udp_hdr(skb)->dest; |
| - else if (key->ip.proto == IPPROTO_TCP) |
| - dst = tcp_hdr(skb)->dest; |
| - else if (key->ip.proto == IPPROTO_SCTP) |
| - dst = sctp_hdr(skb)->dest; |
| - else |
| - return; |
| + /* Update the flow key if NAT successful. */ |
| + if (err == NF_ACCEPT) |
| + ovs_nat_update_key(key, skb, maniptype); |
| |
| - key->tp.dst = dst; |
| - } |
| + return err; |
| } |
| |
| /* Returns NF_DROP if the packet should be dropped, NF_ACCEPT otherwise. */ |
| @@ -903,7 +907,7 @@ static int ovs_ct_nat(struct net *net, struct sw_flow_key *key, |
| } else { |
| return NF_ACCEPT; /* Connection is not NATed. */ |
| } |
| - err = ovs_ct_nat_execute(skb, ct, ctinfo, &info->range, maniptype); |
| + err = ovs_ct_nat_execute(skb, ct, ctinfo, &info->range, maniptype, key); |
| |
| if (err == NF_ACCEPT && ct->status & IPS_DST_NAT) { |
| if (ct->status & IPS_SRC_NAT) { |
| @@ -913,17 +917,13 @@ static int ovs_ct_nat(struct net *net, struct sw_flow_key *key, |
| maniptype = NF_NAT_MANIP_SRC; |
| |
| err = ovs_ct_nat_execute(skb, ct, ctinfo, &info->range, |
| - maniptype); |
| + maniptype, key); |
| } else if (CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) { |
| err = ovs_ct_nat_execute(skb, ct, ctinfo, NULL, |
| - NF_NAT_MANIP_SRC); |
| + NF_NAT_MANIP_SRC, key); |
| } |
| } |
| |
| - /* Mark NAT done if successful and update the flow key. */ |
| - if (err == NF_ACCEPT) |
| - ovs_nat_update_key(key, skb, maniptype); |
| - |
| return err; |
| } |
| #else /* !CONFIG_NF_NAT */ |
| -- |
| 2.34.1 |
| |