| 1 | // SPDX-License-Identifier: GPL-2.0-only |
| 2 | /* Support nat functions for openvswitch and used by OVS and TC conntrack. */ |
| 3 | |
| 4 | #include <net/netfilter/nf_nat.h> |
| 5 | |
| 6 | /* Modelled after nf_nat_ipv[46]_fn(). |
| 7 | * range is only used for new, uninitialized NAT state. |
| 8 | * Returns either NF_ACCEPT or NF_DROP. |
| 9 | */ |
| 10 | static int nf_ct_nat_execute(struct sk_buff *skb, struct nf_conn *ct, |
| 11 | enum ip_conntrack_info ctinfo, int *action, |
| 12 | const struct nf_nat_range2 *range, |
| 13 | enum nf_nat_manip_type maniptype) |
| 14 | { |
| 15 | __be16 proto = skb_protocol(skb, skip_vlan: true); |
| 16 | int hooknum, err = NF_ACCEPT; |
| 17 | |
| 18 | /* See HOOK2MANIP(). */ |
| 19 | if (maniptype == NF_NAT_MANIP_SRC) |
| 20 | hooknum = NF_INET_LOCAL_IN; /* Source NAT */ |
| 21 | else |
| 22 | hooknum = NF_INET_LOCAL_OUT; /* Destination NAT */ |
| 23 | |
| 24 | switch (ctinfo) { |
| 25 | case IP_CT_RELATED: |
| 26 | case IP_CT_RELATED_REPLY: |
| 27 | if (proto == htons(ETH_P_IP) && |
| 28 | ip_hdr(skb)->protocol == IPPROTO_ICMP) { |
| 29 | if (!nf_nat_icmp_reply_translation(skb, ct, ctinfo, |
| 30 | hooknum)) |
| 31 | err = NF_DROP; |
| 32 | goto out; |
| 33 | } else if (IS_ENABLED(CONFIG_IPV6) && proto == htons(ETH_P_IPV6)) { |
| 34 | __be16 frag_off; |
| 35 | u8 nexthdr = ipv6_hdr(skb)->nexthdr; |
| 36 | int hdrlen = ipv6_skip_exthdr(skb, |
| 37 | start: sizeof(struct ipv6hdr), |
| 38 | nexthdrp: &nexthdr, frag_offp: &frag_off); |
| 39 | |
| 40 | if (hdrlen >= 0 && nexthdr == IPPROTO_ICMPV6) { |
| 41 | if (!nf_nat_icmpv6_reply_translation(skb, ct, |
| 42 | ctinfo, |
| 43 | hooknum, |
| 44 | hdrlen)) |
| 45 | err = NF_DROP; |
| 46 | goto out; |
| 47 | } |
| 48 | } |
| 49 | /* Non-ICMP, fall thru to initialize if needed. */ |
| 50 | fallthrough; |
| 51 | case IP_CT_NEW: |
| 52 | /* Seen it before? This can happen for loopback, retrans, |
| 53 | * or local packets. |
| 54 | */ |
| 55 | if (!nf_nat_initialized(ct, manip: maniptype)) { |
| 56 | /* Initialize according to the NAT action. */ |
| 57 | err = (range && range->flags & NF_NAT_RANGE_MAP_IPS) |
| 58 | /* Action is set up to establish a new |
| 59 | * mapping. |
| 60 | */ |
| 61 | ? nf_nat_setup_info(ct, range, maniptype) |
| 62 | : nf_nat_alloc_null_binding(ct, hooknum); |
| 63 | if (err != NF_ACCEPT) |
| 64 | goto out; |
| 65 | } |
| 66 | break; |
| 67 | |
| 68 | case IP_CT_ESTABLISHED: |
| 69 | case IP_CT_ESTABLISHED_REPLY: |
| 70 | break; |
| 71 | |
| 72 | default: |
| 73 | err = NF_DROP; |
| 74 | goto out; |
| 75 | } |
| 76 | |
| 77 | err = nf_nat_packet(ct, ctinfo, hooknum, skb); |
| 78 | out: |
| 79 | if (err == NF_ACCEPT) |
| 80 | *action |= BIT(maniptype); |
| 81 | |
| 82 | return err; |
| 83 | } |
| 84 | |
| 85 | int nf_ct_nat(struct sk_buff *skb, struct nf_conn *ct, |
| 86 | enum ip_conntrack_info ctinfo, int *action, |
| 87 | const struct nf_nat_range2 *range, bool commit) |
| 88 | { |
| 89 | enum nf_nat_manip_type maniptype; |
| 90 | int err, ct_action = *action; |
| 91 | |
| 92 | *action = 0; |
| 93 | |
| 94 | /* Add NAT extension if not confirmed yet. */ |
| 95 | if (!nf_ct_is_confirmed(ct) && !nf_ct_nat_ext_add(ct)) |
| 96 | return NF_DROP; /* Can't NAT. */ |
| 97 | |
| 98 | if (ctinfo != IP_CT_NEW && (ct->status & IPS_NAT_MASK) && |
| 99 | (ctinfo != IP_CT_RELATED || commit)) { |
| 100 | /* NAT an established or related connection like before. */ |
| 101 | if (CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY) |
| 102 | /* This is the REPLY direction for a connection |
| 103 | * for which NAT was applied in the forward |
| 104 | * direction. Do the reverse NAT. |
| 105 | */ |
| 106 | maniptype = ct->status & IPS_SRC_NAT |
| 107 | ? NF_NAT_MANIP_DST : NF_NAT_MANIP_SRC; |
| 108 | else |
| 109 | maniptype = ct->status & IPS_SRC_NAT |
| 110 | ? NF_NAT_MANIP_SRC : NF_NAT_MANIP_DST; |
| 111 | } else if (ct_action & BIT(NF_NAT_MANIP_SRC)) { |
| 112 | maniptype = NF_NAT_MANIP_SRC; |
| 113 | } else if (ct_action & BIT(NF_NAT_MANIP_DST)) { |
| 114 | maniptype = NF_NAT_MANIP_DST; |
| 115 | } else { |
| 116 | return NF_ACCEPT; |
| 117 | } |
| 118 | |
| 119 | err = nf_ct_nat_execute(skb, ct, ctinfo, action, range, maniptype); |
| 120 | if (err == NF_ACCEPT && ct->status & IPS_DST_NAT) { |
| 121 | if (ct->status & IPS_SRC_NAT) { |
| 122 | if (maniptype == NF_NAT_MANIP_SRC) |
| 123 | maniptype = NF_NAT_MANIP_DST; |
| 124 | else |
| 125 | maniptype = NF_NAT_MANIP_SRC; |
| 126 | |
| 127 | err = nf_ct_nat_execute(skb, ct, ctinfo, action, range, |
| 128 | maniptype); |
| 129 | } else if (CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) { |
| 130 | err = nf_ct_nat_execute(skb, ct, ctinfo, action, NULL, |
| 131 | maniptype: NF_NAT_MANIP_SRC); |
| 132 | } |
| 133 | } |
| 134 | return err; |
| 135 | } |
| 136 | EXPORT_SYMBOL_GPL(nf_ct_nat); |
| 137 | |