File Index
Files and directories
Sign in
FILE

ip6t_NPT.c

File preview with syntax highlighting.

linux-6.19.14/net/ipv6/netfilter/ip6t_NPT.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Copyright (c) 2011, 2012 Patrick McHardy <kaber@trash.net>
  4 */
  5
  6#include <linux/module.h>
  7#include <linux/skbuff.h>
  8#include <linux/ipv6.h>
  9#include <net/ipv6.h>
 10#include <linux/netfilter.h>
 11#include <linux/netfilter_ipv6.h>
 12#include <linux/netfilter_ipv6/ip6t_NPT.h>
 13#include <linux/netfilter/x_tables.h>
 14
 15static int ip6t_npt_checkentry(const struct xt_tgchk_param *par)
 16{
 17	struct ip6t_npt_tginfo *npt = par->targinfo;
 18	struct in6_addr pfx;
 19	__wsum src_sum, dst_sum;
 20
 21	if (npt->src_pfx_len > 64 || npt->dst_pfx_len > 64)
 22		return -EINVAL;
 23
 24	/* Ensure that LSB of prefix is zero */
 25	ipv6_addr_prefix(&pfx, &npt->src_pfx.in6, npt->src_pfx_len);
 26	if (!ipv6_addr_equal(&pfx, &npt->src_pfx.in6))
 27		return -EINVAL;
 28	ipv6_addr_prefix(&pfx, &npt->dst_pfx.in6, npt->dst_pfx_len);
 29	if (!ipv6_addr_equal(&pfx, &npt->dst_pfx.in6))
 30		return -EINVAL;
 31
 32	src_sum = csum_partial(&npt->src_pfx.in6, sizeof(npt->src_pfx.in6), 0);
 33	dst_sum = csum_partial(&npt->dst_pfx.in6, sizeof(npt->dst_pfx.in6), 0);
 34
 35	npt->adjustment = ~csum_fold(csum_sub(src_sum, dst_sum));
 36	return 0;
 37}
 38
 39static bool ip6t_npt_map_pfx(const struct ip6t_npt_tginfo *npt,
 40			     struct in6_addr *addr)
 41{
 42	unsigned int pfx_len;
 43	unsigned int i, idx;
 44	__be32 mask;
 45	__sum16 sum;
 46
 47	pfx_len = max(npt->src_pfx_len, npt->dst_pfx_len);
 48	for (i = 0; i < pfx_len; i += 32) {
 49		if (pfx_len - i >= 32)
 50			mask = 0;
 51		else
 52			mask = htonl((1 << (i - pfx_len + 32)) - 1);
 53
 54		idx = i / 32;
 55		addr->s6_addr32[idx] &= mask;
 56		addr->s6_addr32[idx] |= ~mask & npt->dst_pfx.in6.s6_addr32[idx];
 57	}
 58
 59	if (pfx_len <= 48)
 60		idx = 3;
 61	else {
 62		for (idx = 4; idx < ARRAY_SIZE(addr->s6_addr16); idx++) {
 63			if ((__force __sum16)addr->s6_addr16[idx] !=
 64			    CSUM_MANGLED_0)
 65				break;
 66		}
 67		if (idx == ARRAY_SIZE(addr->s6_addr16))
 68			return false;
 69	}
 70
 71	sum = ~csum_fold(csum_add(csum_unfold((__force __sum16)addr->s6_addr16[idx]),
 72				  csum_unfold(npt->adjustment)));
 73	if (sum == CSUM_MANGLED_0)
 74		sum = 0;
 75	*(__force __sum16 *)&addr->s6_addr16[idx] = sum;
 76
 77	return true;
 78}
 79
 80static struct ipv6hdr *icmpv6_bounced_ipv6hdr(struct sk_buff *skb,
 81					      struct ipv6hdr *_bounced_hdr)
 82{
 83	if (ipv6_hdr(skb)->nexthdr != IPPROTO_ICMPV6)
 84		return NULL;
 85
 86	if (!icmpv6_is_err(icmp6_hdr(skb)->icmp6_type))
 87		return NULL;
 88
 89	return skb_header_pointer(skb,
 90				  skb_transport_offset(skb) + sizeof(struct icmp6hdr),
 91				  sizeof(struct ipv6hdr),
 92				  _bounced_hdr);
 93}
 94
 95static unsigned int
 96ip6t_snpt_tg(struct sk_buff *skb, const struct xt_action_param *par)
 97{
 98	const struct ip6t_npt_tginfo *npt = par->targinfo;
 99	struct ipv6hdr _bounced_hdr;
100	struct ipv6hdr *bounced_hdr;
101	struct in6_addr bounced_pfx;
102
103	if (!ip6t_npt_map_pfx(npt, &ipv6_hdr(skb)->saddr)) {
104		icmpv6_send(skb, ICMPV6_PARAMPROB, ICMPV6_HDR_FIELD,
105			    offsetof(struct ipv6hdr, saddr));
106		return NF_DROP;
107	}
108
109	/* rewrite dst addr of bounced packet which was sent to dst range */
110	bounced_hdr = icmpv6_bounced_ipv6hdr(skb, &_bounced_hdr);
111	if (bounced_hdr) {
112		ipv6_addr_prefix(&bounced_pfx, &bounced_hdr->daddr, npt->src_pfx_len);
113		if (ipv6_addr_cmp(&bounced_pfx, &npt->src_pfx.in6) == 0)
114			ip6t_npt_map_pfx(npt, &bounced_hdr->daddr);
115	}
116
117	return XT_CONTINUE;
118}
119
120static unsigned int
121ip6t_dnpt_tg(struct sk_buff *skb, const struct xt_action_param *par)
122{
123	const struct ip6t_npt_tginfo *npt = par->targinfo;
124	struct ipv6hdr _bounced_hdr;
125	struct ipv6hdr *bounced_hdr;
126	struct in6_addr bounced_pfx;
127
128	if (!ip6t_npt_map_pfx(npt, &ipv6_hdr(skb)->daddr)) {
129		icmpv6_send(skb, ICMPV6_PARAMPROB, ICMPV6_HDR_FIELD,
130			    offsetof(struct ipv6hdr, daddr));
131		return NF_DROP;
132	}
133
134	/* rewrite src addr of bounced packet which was sent from dst range */
135	bounced_hdr = icmpv6_bounced_ipv6hdr(skb, &_bounced_hdr);
136	if (bounced_hdr) {
137		ipv6_addr_prefix(&bounced_pfx, &bounced_hdr->saddr, npt->src_pfx_len);
138		if (ipv6_addr_cmp(&bounced_pfx, &npt->src_pfx.in6) == 0)
139			ip6t_npt_map_pfx(npt, &bounced_hdr->saddr);
140	}
141
142	return XT_CONTINUE;
143}
144
145static struct xt_target ip6t_npt_target_reg[] __read_mostly = {
146	{
147		.name		= "SNPT",
148		.table		= "mangle",
149		.target		= ip6t_snpt_tg,
150		.targetsize	= sizeof(struct ip6t_npt_tginfo),
151		.usersize	= offsetof(struct ip6t_npt_tginfo, adjustment),
152		.checkentry	= ip6t_npt_checkentry,
153		.family		= NFPROTO_IPV6,
154		.hooks		= (1 << NF_INET_LOCAL_IN) |
155				  (1 << NF_INET_POST_ROUTING),
156		.me		= THIS_MODULE,
157	},
158	{
159		.name		= "DNPT",
160		.table		= "mangle",
161		.target		= ip6t_dnpt_tg,
162		.targetsize	= sizeof(struct ip6t_npt_tginfo),
163		.usersize	= offsetof(struct ip6t_npt_tginfo, adjustment),
164		.checkentry	= ip6t_npt_checkentry,
165		.family		= NFPROTO_IPV6,
166		.hooks		= (1 << NF_INET_PRE_ROUTING) |
167				  (1 << NF_INET_LOCAL_OUT),
168		.me		= THIS_MODULE,
169	},
170};
171
172static int __init ip6t_npt_init(void)
173{
174	return xt_register_targets(ip6t_npt_target_reg,
175				   ARRAY_SIZE(ip6t_npt_target_reg));
176}
177
178static void __exit ip6t_npt_exit(void)
179{
180	xt_unregister_targets(ip6t_npt_target_reg,
181			      ARRAY_SIZE(ip6t_npt_target_reg));
182}
183
184module_init(ip6t_npt_init);
185module_exit(ip6t_npt_exit);
186
187MODULE_LICENSE("GPL");
188MODULE_DESCRIPTION("IPv6-to-IPv6 Network Prefix Translation (RFC 6296)");
189MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
190MODULE_ALIAS("ip6t_SNPT");
191MODULE_ALIAS("ip6t_DNPT");
~/ Read-only file preview.