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 | |