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 */ 10static 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, 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 sizeof(struct ipv6hdr), 38 &nexthdr, &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, 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); 78out: 79 if (err == NF_ACCEPT) 80 *action |= BIT(maniptype); 81 82 return err; 83} 84 85int 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 NF_NAT_MANIP_SRC); 132 } 133 } 134 return err; 135} 136EXPORT_SYMBOL_GPL(nf_ct_nat); 137