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