xref: /freebsd/sys/netpfil/pf/inet_nat64.c (revision fcdb520c1b4e1a5d5a2e54cb916dccbc848d32ba)
1 /*	$OpenBSD: inet_nat64.c,v 1.1 2011/10/13 18:23:40 claudio Exp $	*/
2 /*	$vantronix: inet_nat64.c,v 1.2 2011/02/28 14:57:58 mike Exp $	*/
3 
4 /*
5  * Copyright (c) 2011 Reyk Floeter <reyk@vantronix.net>
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include <sys/param.h>
21 #include <sys/socket.h>
22 #include <sys/mbuf.h>
23 #include <netinet/in.h>
24 #include <net/pfvar.h>
25 
26 union inet_nat64_addr {
27 	u_int32_t	 u32[4];
28 	u_int8_t	 u8[16];
29 };
30 
31 static u_int32_t
inet_nat64_mask(u_int32_t src,u_int32_t pfx,u_int8_t pfxlen)32 inet_nat64_mask(u_int32_t src, u_int32_t pfx, u_int8_t pfxlen)
33 {
34 	u_int32_t	u32;
35 	if (pfxlen == 0)
36 		return (src);
37 	else if (pfxlen > 32)
38 		pfxlen = 32;
39 	u32 =
40 	    (src & ~htonl(0xffffffff << (32 - pfxlen))) |
41 	    (pfx & htonl(0xffffffff << (32 - pfxlen)));
42 	return (u32);
43 
44 }
45 
46 int
inet_nat64(int af,const void * src,void * dst,const void * pfx,u_int8_t pfxlen)47 inet_nat64(int af, const void *src, void *dst,
48     const void *pfx, u_int8_t pfxlen)
49 {
50 	switch (af) {
51 	case AF_INET:
52 		return (inet_nat64_inet(src, dst, pfx, pfxlen));
53 	case AF_INET6:
54 		return (inet_nat64_inet6(src, dst, pfx, pfxlen));
55 	default:
56 #ifndef _KERNEL
57 		errno = EAFNOSUPPORT;
58 #endif
59 		return (-1);
60 	}
61 	/* NOTREACHED */
62 }
63 
64 int
inet_nat64_inet(const void * src,void * dst,const void * pfx,u_int8_t pfxlen)65 inet_nat64_inet(const void *src, void *dst, const void *pfx, u_int8_t pfxlen)
66 {
67 	const union inet_nat64_addr	*s = src;
68 	const union inet_nat64_addr	*p = pfx;
69 	union inet_nat64_addr		*d = dst;
70 	int				 i, j;
71 
72 	switch (pfxlen) {
73 	case 32:
74 	case 40:
75 	case 48:
76 	case 56:
77 	case 64:
78 	case 96:
79 		i = pfxlen / 8;
80 		break;
81 	default:
82 		if (pfxlen < 96 || pfxlen > 128) {
83 #ifndef _KERNEL
84 			errno = EINVAL;
85 #endif
86 			return (-1);
87 		}
88 
89 		/* as an extension, mask out any other bits */
90 		d->u32[0] = inet_nat64_mask(s->u32[3], p->u32[3],
91 		    (u_int8_t)(32 - (128 - pfxlen)));
92 		return (0);
93 	}
94 
95 	/* fill the octets with the source and skip reserved octet 8 */
96 	for (j = 0; j < 4; j++) {
97 		if (i == 8)
98 			i++;
99 		d->u8[j] = s->u8[i++];
100 	}
101 
102 	return (0);
103 }
104 
105 int
inet_nat64_inet6(const void * src,void * dst,const void * pfx,u_int8_t pfxlen)106 inet_nat64_inet6(const void *src, void *dst, const void *pfx, u_int8_t pfxlen)
107 {
108 	const union inet_nat64_addr	*s = src;
109 	const union inet_nat64_addr	*p = pfx;
110 	union inet_nat64_addr		*d = dst;
111 	int				 i, j;
112 
113 	/* first copy the prefix octets to the destination */
114 	*d = *p;
115 
116 	switch (pfxlen) {
117 	case 32:
118 	case 40:
119 	case 48:
120 	case 56:
121 	case 64:
122 	case 96:
123 		i = pfxlen / 8;
124 		break;
125 	default:
126 		if (pfxlen < 96 || pfxlen > 128) {
127 #ifndef _KERNEL
128 			errno = EINVAL;
129 #endif
130 			return (-1);
131 		}
132 
133 		/* as an extension, mask out any other bits */
134 		d->u32[3] = inet_nat64_mask(s->u32[0], p->u32[3],
135 		    (u_int8_t)(32 - (128 - pfxlen)));
136 		return (0);
137 	}
138 
139 	/* octet 8 is reserved and must be set to zero */
140 	d->u8[8] = 0;
141 
142 	/* fill the other octets with the source and skip octet 8 */
143 	for (j = 0; j < 4; j++) {
144 		if (i == 8)
145 			i++;
146 		d->u8[i++] = s->u8[j];
147 	}
148 
149 	return (0);
150 }
151 
152 int
inet_nat46(int af,const void * src,void * dst,const void * pfx,u_int8_t pfxlen)153 inet_nat46(int af, const void *src, void *dst,
154     const void *pfx, u_int8_t pfxlen)
155 {
156 	if (pfxlen > 32) {
157 #ifndef _KERNEL
158 		errno = EINVAL;
159 #endif
160 		return (-1);
161 	}
162 
163 	switch (af) {
164 	case AF_INET:
165 		return (inet_nat46_inet(src, dst, pfx, pfxlen));
166 	case AF_INET6:
167 		return (inet_nat46_inet6(src, dst, pfx, pfxlen));
168 	default:
169 #ifndef _KERNEL
170 		errno = EAFNOSUPPORT;
171 #endif
172 		return (-1);
173 	}
174 	/* NOTREACHED */
175 }
176 
177 int
inet_nat46_inet(const void * src,void * dst,const void * pfx,u_int8_t pfxlen)178 inet_nat46_inet(const void *src, void *dst, const void *pfx, u_int8_t pfxlen)
179 {
180 	const union inet_nat64_addr	*s = src;
181 	const union inet_nat64_addr	*p = pfx;
182 	union inet_nat64_addr		*d = dst;
183 
184 	/* set the remaining bits to the source */
185 	d->u32[0] = inet_nat64_mask(s->u32[3], p->u32[0], pfxlen);
186 
187 	return (0);
188 }
189 
190 int
inet_nat46_inet6(const void * src,void * dst,const void * pfx,u_int8_t pfxlen)191 inet_nat46_inet6(const void *src, void *dst, const void *pfx, u_int8_t pfxlen)
192 {
193 	const union inet_nat64_addr	*s = src;
194 	const union inet_nat64_addr	*p = pfx;
195 	union inet_nat64_addr		*d = dst;
196 
197 	/* set the initial octets to zero */
198 	d->u32[0] = d->u32[1] = d->u32[2] = 0;
199 
200 	/* now set the remaining bits to the source */
201 	d->u32[3] = inet_nat64_mask(s->u32[0], p->u32[0], pfxlen);
202 
203 	return (0);
204 }
205