1ef8d499eSDavid van Moolenbroek /* LWIP service - addr.c - socket address verification and conversion */
2ef8d499eSDavid van Moolenbroek
3ef8d499eSDavid van Moolenbroek #include "lwip.h"
4ef8d499eSDavid van Moolenbroek
5ef8d499eSDavid van Moolenbroek /*
6ef8d499eSDavid van Moolenbroek * Return TRUE if the given socket address is of type AF_UNSPEC, or FALSE
7ef8d499eSDavid van Moolenbroek * otherwise.
8ef8d499eSDavid van Moolenbroek */
9ef8d499eSDavid van Moolenbroek int
addr_is_unspec(const struct sockaddr * addr,socklen_t addr_len)10ef8d499eSDavid van Moolenbroek addr_is_unspec(const struct sockaddr * addr, socklen_t addr_len)
11ef8d499eSDavid van Moolenbroek {
12ef8d499eSDavid van Moolenbroek
13ef8d499eSDavid van Moolenbroek return (addr_len >= offsetof(struct sockaddr, sa_data) &&
14ef8d499eSDavid van Moolenbroek addr->sa_family == AF_UNSPEC);
15ef8d499eSDavid van Moolenbroek }
16ef8d499eSDavid van Moolenbroek
17ef8d499eSDavid van Moolenbroek /*
18ef8d499eSDavid van Moolenbroek * Check whether the given multicast address is generally valid. This check
19ef8d499eSDavid van Moolenbroek * should not be moved into addr_get_inet(), as we do not want to forbid
20ef8d499eSDavid van Moolenbroek * creating routes for such addresses, for example. We do however apply the
21ef8d499eSDavid van Moolenbroek * restrictions here to all provided source and destination addresses. Return
22ef8d499eSDavid van Moolenbroek * TRUE if the address is an acceptable multicast address, or FALSE otherwise.
23ef8d499eSDavid van Moolenbroek */
24ef8d499eSDavid van Moolenbroek int
addr_is_valid_multicast(const ip_addr_t * ipaddr)25ef8d499eSDavid van Moolenbroek addr_is_valid_multicast(const ip_addr_t * ipaddr)
26ef8d499eSDavid van Moolenbroek {
27ef8d499eSDavid van Moolenbroek uint8_t scope;
28ef8d499eSDavid van Moolenbroek
29ef8d499eSDavid van Moolenbroek assert(ip_addr_ismulticast(ipaddr));
30ef8d499eSDavid van Moolenbroek
31ef8d499eSDavid van Moolenbroek /* We apply restrictions to IPv6 multicast addresses only. */
32ef8d499eSDavid van Moolenbroek if (IP_IS_V6(ipaddr)) {
33ef8d499eSDavid van Moolenbroek scope = ip6_addr_multicast_scope(ip_2_ip6(ipaddr));
34ef8d499eSDavid van Moolenbroek
35ef8d499eSDavid van Moolenbroek if (scope == IP6_MULTICAST_SCOPE_RESERVED0 ||
36ef8d499eSDavid van Moolenbroek scope == IP6_MULTICAST_SCOPE_RESERVEDF)
37ef8d499eSDavid van Moolenbroek return FALSE;
38ef8d499eSDavid van Moolenbroek
39ef8d499eSDavid van Moolenbroek /*
40ef8d499eSDavid van Moolenbroek * We do not impose restrictions on the three defined embedded
41ef8d499eSDavid van Moolenbroek * flags, even though we put no effort into supporting them,
42ef8d499eSDavid van Moolenbroek * especially in terms of automatically creating routes for
43ef8d499eSDavid van Moolenbroek * all cases. We do force the fourth flag to be zero.
44ef8d499eSDavid van Moolenbroek * Unfortunately there is no lwIP macro to check for this flag.
45ef8d499eSDavid van Moolenbroek */
46ef8d499eSDavid van Moolenbroek if (ip_2_ip6(ipaddr)->addr[0] & PP_HTONL(0x00800000UL))
47ef8d499eSDavid van Moolenbroek return FALSE;
48ef8d499eSDavid van Moolenbroek
49ef8d499eSDavid van Moolenbroek /* Prevent KAME-embedded zone IDs from entering the system. */
50ef8d499eSDavid van Moolenbroek if (ip6_addr_has_scope(ip_2_ip6(ipaddr), IP6_UNKNOWN) &&
51ef8d499eSDavid van Moolenbroek (ip_2_ip6(ipaddr)->addr[0] & PP_HTONL(0x0000ffffUL)))
52ef8d499eSDavid van Moolenbroek return FALSE;
53ef8d499eSDavid van Moolenbroek }
54ef8d499eSDavid van Moolenbroek
55ef8d499eSDavid van Moolenbroek return TRUE;
56ef8d499eSDavid van Moolenbroek }
57ef8d499eSDavid van Moolenbroek
58ef8d499eSDavid van Moolenbroek /*
59ef8d499eSDavid van Moolenbroek * Load a sockaddr structure, as copied from userland, as a lwIP-style IP
60ef8d499eSDavid van Moolenbroek * address and (optionally) a port number. The expected type of IP address is
61ef8d499eSDavid van Moolenbroek * given as 'type', which must be one of IPADDR_TYPE_{V4,ANY,V6}. If it is
62ef8d499eSDavid van Moolenbroek * IPADDR_TYPE_V4, 'addr' is expected to point to a sockaddr_in structure. If
63ef8d499eSDavid van Moolenbroek * it is IPADDR_TYPE_{ANY,V6}, 'addr' is expected to point to a sockaddr_in6
64ef8d499eSDavid van Moolenbroek * structure. For the _ANY case, the result will be an _ANY address only if it
65ef8d499eSDavid van Moolenbroek * is the unspecified (all-zeroes) address and a _V6 address in all other
66ef8d499eSDavid van Moolenbroek * cases. For the _V6 case, the result will always be a _V6 address. The
67ef8d499eSDavid van Moolenbroek * length of the structure pointed to by 'addr' is given as 'addr_len'. If the
68ef8d499eSDavid van Moolenbroek * boolean 'kame' flag is set, addresses will be interpreted to be KAME style,
69ef8d499eSDavid van Moolenbroek * meaning that for scoped IPv6 addresses, the zone is embedded in the address
70ef8d499eSDavid van Moolenbroek * rather than given in sin6_scope_id. On success, store the resulting IP
71ef8d499eSDavid van Moolenbroek * address in 'ipaddr'. If 'port' is not NULL, store the port number in it;
72ef8d499eSDavid van Moolenbroek * otherwise, ignore the port number. On any parsing failure, return an
73ef8d499eSDavid van Moolenbroek * appropriate negative error code.
74ef8d499eSDavid van Moolenbroek */
75ef8d499eSDavid van Moolenbroek int
addr_get_inet(const struct sockaddr * addr,socklen_t addr_len,uint8_t type,ip_addr_t * ipaddr,int kame,uint16_t * port)76ef8d499eSDavid van Moolenbroek addr_get_inet(const struct sockaddr * addr, socklen_t addr_len, uint8_t type,
77ef8d499eSDavid van Moolenbroek ip_addr_t * ipaddr, int kame, uint16_t * port)
78ef8d499eSDavid van Moolenbroek {
79ef8d499eSDavid van Moolenbroek struct sockaddr_in sin;
80ef8d499eSDavid van Moolenbroek struct sockaddr_in6 sin6;
81ef8d499eSDavid van Moolenbroek ip6_addr_t *ip6addr;
82ef8d499eSDavid van Moolenbroek uint32_t ifindex;
83ef8d499eSDavid van Moolenbroek
84ef8d499eSDavid van Moolenbroek switch (type) {
85ef8d499eSDavid van Moolenbroek case IPADDR_TYPE_V4:
86ef8d499eSDavid van Moolenbroek if (addr_len != sizeof(sin))
87ef8d499eSDavid van Moolenbroek return EINVAL;
88ef8d499eSDavid van Moolenbroek
89ef8d499eSDavid van Moolenbroek /*
90ef8d499eSDavid van Moolenbroek * Getting around strict aliasing problems. Oh, the irony of
91ef8d499eSDavid van Moolenbroek * doing an extra memcpy so that the compiler can do a better
92ef8d499eSDavid van Moolenbroek * job at optimizing..
93ef8d499eSDavid van Moolenbroek */
94ef8d499eSDavid van Moolenbroek memcpy(&sin, addr, sizeof(sin));
95ef8d499eSDavid van Moolenbroek
96ef8d499eSDavid van Moolenbroek if (sin.sin_family != AF_INET)
97ef8d499eSDavid van Moolenbroek return EAFNOSUPPORT;
98ef8d499eSDavid van Moolenbroek
99ef8d499eSDavid van Moolenbroek ip_addr_set_ip4_u32(ipaddr, sin.sin_addr.s_addr);
100ef8d499eSDavid van Moolenbroek
101ef8d499eSDavid van Moolenbroek if (port != NULL)
102ef8d499eSDavid van Moolenbroek *port = ntohs(sin.sin_port);
103ef8d499eSDavid van Moolenbroek
104ef8d499eSDavid van Moolenbroek return OK;
105ef8d499eSDavid van Moolenbroek
106ef8d499eSDavid van Moolenbroek case IPADDR_TYPE_ANY:
107ef8d499eSDavid van Moolenbroek case IPADDR_TYPE_V6:
108ef8d499eSDavid van Moolenbroek if (addr_len != sizeof(sin6))
109ef8d499eSDavid van Moolenbroek return EINVAL;
110ef8d499eSDavid van Moolenbroek
111ef8d499eSDavid van Moolenbroek /* Again, strict aliasing.. */
112ef8d499eSDavid van Moolenbroek memcpy(&sin6, addr, sizeof(sin6));
113ef8d499eSDavid van Moolenbroek
114ef8d499eSDavid van Moolenbroek if (sin6.sin6_family != AF_INET6)
115ef8d499eSDavid van Moolenbroek return EAFNOSUPPORT;
116ef8d499eSDavid van Moolenbroek
117ef8d499eSDavid van Moolenbroek memset(ipaddr, 0, sizeof(*ipaddr));
118ef8d499eSDavid van Moolenbroek
119ef8d499eSDavid van Moolenbroek /*
120ef8d499eSDavid van Moolenbroek * This is a bit ugly, but NetBSD does not expose s6_addr32 and
121ef8d499eSDavid van Moolenbroek * s6_addr is a series of bytes, which is a mismatch for lwIP.
122ef8d499eSDavid van Moolenbroek * The alternative would be another memcpy..
123ef8d499eSDavid van Moolenbroek */
124ef8d499eSDavid van Moolenbroek ip6addr = ip_2_ip6(ipaddr);
125ef8d499eSDavid van Moolenbroek assert(sizeof(ip6addr->addr) == sizeof(sin6.sin6_addr));
126ef8d499eSDavid van Moolenbroek memcpy(ip6addr->addr, &sin6.sin6_addr, sizeof(ip6addr->addr));
127ef8d499eSDavid van Moolenbroek
128ef8d499eSDavid van Moolenbroek /*
129ef8d499eSDavid van Moolenbroek * If the address may have a scope, extract the zone ID.
130ef8d499eSDavid van Moolenbroek * Where the zone ID is depends on the 'kame' parameter: KAME-
131ef8d499eSDavid van Moolenbroek * style addresses have it embedded within the address, whereas
132ef8d499eSDavid van Moolenbroek * non-KAME addresses use the (misnamed) sin6_scope_id field.
133ef8d499eSDavid van Moolenbroek */
134ef8d499eSDavid van Moolenbroek if (ip6_addr_has_scope(ip6addr, IP6_UNKNOWN)) {
135ef8d499eSDavid van Moolenbroek if (kame) {
136ef8d499eSDavid van Moolenbroek ifindex =
137ef8d499eSDavid van Moolenbroek ntohl(ip6addr->addr[0]) & 0x0000ffffUL;
138ef8d499eSDavid van Moolenbroek
139ef8d499eSDavid van Moolenbroek ip6addr->addr[0] &= PP_HTONL(0xffff0000UL);
140ef8d499eSDavid van Moolenbroek } else {
141ef8d499eSDavid van Moolenbroek /*
142ef8d499eSDavid van Moolenbroek * Reject KAME-style addresses for normal
143ef8d499eSDavid van Moolenbroek * socket calls, to save ourselves the trouble
144ef8d499eSDavid van Moolenbroek * of mixed address styles elsewhere.
145ef8d499eSDavid van Moolenbroek */
146ef8d499eSDavid van Moolenbroek if (ip6addr->addr[0] & PP_HTONL(0x0000ffffUL))
147ef8d499eSDavid van Moolenbroek return EINVAL;
148ef8d499eSDavid van Moolenbroek
149ef8d499eSDavid van Moolenbroek ifindex = sin6.sin6_scope_id;
150ef8d499eSDavid van Moolenbroek }
151ef8d499eSDavid van Moolenbroek
152ef8d499eSDavid van Moolenbroek /*
153ef8d499eSDavid van Moolenbroek * Reject invalid zone IDs. This also enforces that
154ef8d499eSDavid van Moolenbroek * no zone IDs wider than eight bits enter the system.
155ef8d499eSDavid van Moolenbroek * As a side effect, it is not possible to add routes
156ef8d499eSDavid van Moolenbroek * for invalid zones, but that should be no problem.
157ef8d499eSDavid van Moolenbroek */
158ef8d499eSDavid van Moolenbroek if (ifindex != 0 &&
159ef8d499eSDavid van Moolenbroek ifdev_get_by_index(ifindex) == NULL)
160ef8d499eSDavid van Moolenbroek return ENXIO;
161ef8d499eSDavid van Moolenbroek
162ef8d499eSDavid van Moolenbroek ip6_addr_set_zone(ip6addr, ifindex);
163ef8d499eSDavid van Moolenbroek } else
164ef8d499eSDavid van Moolenbroek ip6_addr_clear_zone(ip6addr);
165ef8d499eSDavid van Moolenbroek
166ef8d499eSDavid van Moolenbroek /*
167ef8d499eSDavid van Moolenbroek * Set the type to ANY if it was ANY and the address itself is
168ef8d499eSDavid van Moolenbroek * ANY as well. Otherwise, we are binding to a specific IPv6
169ef8d499eSDavid van Moolenbroek * address, so IPV6_V6ONLY stops being relevant and we should
170ef8d499eSDavid van Moolenbroek * leave the address set to V6. Destination addresses for ANY
171ef8d499eSDavid van Moolenbroek * are set to V6 elsewhere.
172ef8d499eSDavid van Moolenbroek */
173ef8d499eSDavid van Moolenbroek if (type == IPADDR_TYPE_ANY && ip6_addr_isany(ip6addr))
174ef8d499eSDavid van Moolenbroek IP_SET_TYPE(ipaddr, type);
175ef8d499eSDavid van Moolenbroek else
176ef8d499eSDavid van Moolenbroek IP_SET_TYPE(ipaddr, IPADDR_TYPE_V6);
177ef8d499eSDavid van Moolenbroek
178ef8d499eSDavid van Moolenbroek if (port != NULL)
179ef8d499eSDavid van Moolenbroek *port = ntohs(sin6.sin6_port);
180ef8d499eSDavid van Moolenbroek
181ef8d499eSDavid van Moolenbroek return OK;
182ef8d499eSDavid van Moolenbroek
183ef8d499eSDavid van Moolenbroek default:
184ef8d499eSDavid van Moolenbroek return EAFNOSUPPORT;
185ef8d499eSDavid van Moolenbroek }
186ef8d499eSDavid van Moolenbroek }
187ef8d499eSDavid van Moolenbroek
188ef8d499eSDavid van Moolenbroek /*
189ef8d499eSDavid van Moolenbroek * Store an lwIP-style IP address and port number as a sockaddr structure
190ef8d499eSDavid van Moolenbroek * (sockaddr_in or sockaddr_in6, depending on the given IP address) to be
191ef8d499eSDavid van Moolenbroek * copied to userland. The result is stored in the buffer pointed to by
192ef8d499eSDavid van Moolenbroek * 'addr'. Before the call, 'addr_len' must be set to the size of this buffer.
193ef8d499eSDavid van Moolenbroek * This is an internal check to prevent buffer overflows, and must not be used
194ef8d499eSDavid van Moolenbroek * to validate input, since a mismatch will trigger a panic. After the call,
195ef8d499eSDavid van Moolenbroek * 'addr_len' will be set to the size of the resulting structure. The lwIP-
196ef8d499eSDavid van Moolenbroek * style address is given as 'ipaddr'. If the boolean 'kame' flag is set, the
197ef8d499eSDavid van Moolenbroek * address will be stored KAME-style, meaning that for scoped IPv6 addresses,
198ef8d499eSDavid van Moolenbroek * the address zone will be stored embedded in the address rather than in
199ef8d499eSDavid van Moolenbroek * sin6_scope_id. If relevant, 'port' contains the port number in host-byte
200ef8d499eSDavid van Moolenbroek * order; otherwise it should be set to zone.
201ef8d499eSDavid van Moolenbroek */
202ef8d499eSDavid van Moolenbroek void
addr_put_inet(struct sockaddr * addr,socklen_t * addr_len,const ip_addr_t * ipaddr,int kame,uint16_t port)203ef8d499eSDavid van Moolenbroek addr_put_inet(struct sockaddr * addr, socklen_t * addr_len,
204ef8d499eSDavid van Moolenbroek const ip_addr_t * ipaddr, int kame, uint16_t port)
205ef8d499eSDavid van Moolenbroek {
206ef8d499eSDavid van Moolenbroek struct sockaddr_in sin;
207ef8d499eSDavid van Moolenbroek struct sockaddr_in6 sin6;
208ef8d499eSDavid van Moolenbroek const ip6_addr_t *ip6addr;
209ef8d499eSDavid van Moolenbroek uint32_t zone;
210ef8d499eSDavid van Moolenbroek
211ef8d499eSDavid van Moolenbroek switch (IP_GET_TYPE(ipaddr)) {
212ef8d499eSDavid van Moolenbroek case IPADDR_TYPE_V4:
213ef8d499eSDavid van Moolenbroek if (*addr_len < sizeof(sin))
214ef8d499eSDavid van Moolenbroek panic("provided address buffer too small");
215ef8d499eSDavid van Moolenbroek
216ef8d499eSDavid van Moolenbroek memset(&sin, 0, sizeof(sin));
217ef8d499eSDavid van Moolenbroek
218ef8d499eSDavid van Moolenbroek sin.sin_len = sizeof(sin);
219ef8d499eSDavid van Moolenbroek sin.sin_family = AF_INET;
220ef8d499eSDavid van Moolenbroek sin.sin_port = htons(port);
221ef8d499eSDavid van Moolenbroek sin.sin_addr.s_addr = ip_addr_get_ip4_u32(ipaddr);
222ef8d499eSDavid van Moolenbroek
223ef8d499eSDavid van Moolenbroek memcpy(addr, &sin, sizeof(sin));
224ef8d499eSDavid van Moolenbroek *addr_len = sizeof(sin);
225ef8d499eSDavid van Moolenbroek
226ef8d499eSDavid van Moolenbroek break;
227ef8d499eSDavid van Moolenbroek
228ef8d499eSDavid van Moolenbroek case IPADDR_TYPE_ANY:
229ef8d499eSDavid van Moolenbroek case IPADDR_TYPE_V6:
230ef8d499eSDavid van Moolenbroek if (*addr_len < sizeof(sin6))
231ef8d499eSDavid van Moolenbroek panic("provided address buffer too small");
232ef8d499eSDavid van Moolenbroek
233ef8d499eSDavid van Moolenbroek ip6addr = ip_2_ip6(ipaddr);
234ef8d499eSDavid van Moolenbroek
235ef8d499eSDavid van Moolenbroek memset(&sin6, 0, sizeof(sin6));
236ef8d499eSDavid van Moolenbroek
237ef8d499eSDavid van Moolenbroek sin6.sin6_len = sizeof(sin6);
238ef8d499eSDavid van Moolenbroek sin6.sin6_family = AF_INET6;
239ef8d499eSDavid van Moolenbroek sin6.sin6_port = htons(port);
240ef8d499eSDavid van Moolenbroek memcpy(&sin6.sin6_addr, ip6addr->addr, sizeof(sin6.sin6_addr));
241ef8d499eSDavid van Moolenbroek
242ef8d499eSDavid van Moolenbroek /*
243ef8d499eSDavid van Moolenbroek * If the IPv6 address has a zone set, it must be scoped, and
244ef8d499eSDavid van Moolenbroek * we put the zone in the result. It may occur that a scoped
245ef8d499eSDavid van Moolenbroek * IPv6 address does not have a zone here though, for example
246ef8d499eSDavid van Moolenbroek * if packet routing fails for sendto() with a zoneless address
247ef8d499eSDavid van Moolenbroek * on an unbound socket, resulting in an RTM_MISS message. In
248ef8d499eSDavid van Moolenbroek * such cases, simply leave the zone index blank in the result.
249ef8d499eSDavid van Moolenbroek */
250ef8d499eSDavid van Moolenbroek if (ip6_addr_has_zone(ip6addr)) {
251ef8d499eSDavid van Moolenbroek assert(ip6_addr_has_scope(ip6addr, IP6_UNKNOWN));
252ef8d499eSDavid van Moolenbroek
253ef8d499eSDavid van Moolenbroek zone = ip6_addr_zone(ip6addr);
254ef8d499eSDavid van Moolenbroek assert(zone <= UINT8_MAX);
255ef8d499eSDavid van Moolenbroek
256ef8d499eSDavid van Moolenbroek if (kame)
257ef8d499eSDavid van Moolenbroek sin6.sin6_addr.s6_addr[3] = zone;
258ef8d499eSDavid van Moolenbroek else
259ef8d499eSDavid van Moolenbroek sin6.sin6_scope_id = zone;
260ef8d499eSDavid van Moolenbroek }
261ef8d499eSDavid van Moolenbroek
262ef8d499eSDavid van Moolenbroek memcpy(addr, &sin6, sizeof(sin6));
263ef8d499eSDavid van Moolenbroek *addr_len = sizeof(sin6);
264ef8d499eSDavid van Moolenbroek
265ef8d499eSDavid van Moolenbroek break;
266ef8d499eSDavid van Moolenbroek
267ef8d499eSDavid van Moolenbroek default:
268ef8d499eSDavid van Moolenbroek panic("unknown IP address type: %u", IP_GET_TYPE(ipaddr));
269ef8d499eSDavid van Moolenbroek }
270ef8d499eSDavid van Moolenbroek }
271ef8d499eSDavid van Moolenbroek
272ef8d499eSDavid van Moolenbroek /*
273ef8d499eSDavid van Moolenbroek * Load a link-layer sockaddr structure (sockaddr_dl), as copied from userland,
274ef8d499eSDavid van Moolenbroek * and return the contained name and/or hardware address. The address is
275ef8d499eSDavid van Moolenbroek * provided as 'addr', with length 'addr_len'. On success, return OK. If
276ef8d499eSDavid van Moolenbroek * 'name' is not NULL, it must be of size 'name_max', and will be used to store
277ef8d499eSDavid van Moolenbroek * the (null-terminated) interface name in the given structure if present, or
278ef8d499eSDavid van Moolenbroek * the empty string if not. If 'hwaddr' is not NULL, it will be used to store
279ef8d499eSDavid van Moolenbroek * the hardware address in the given structure, which must in that case be
280ef8d499eSDavid van Moolenbroek * present and exactly 'hwaddr_len' bytes long. On any parsing failure, return
281ef8d499eSDavid van Moolenbroek * an appropriate negative error code.
282ef8d499eSDavid van Moolenbroek */
283ef8d499eSDavid van Moolenbroek int
addr_get_link(const struct sockaddr * addr,socklen_t addr_len,char * name,size_t name_max,uint8_t * hwaddr,size_t hwaddr_len)284ef8d499eSDavid van Moolenbroek addr_get_link(const struct sockaddr * addr, socklen_t addr_len, char * name,
285ef8d499eSDavid van Moolenbroek size_t name_max, uint8_t * hwaddr, size_t hwaddr_len)
286ef8d499eSDavid van Moolenbroek {
287ef8d499eSDavid van Moolenbroek struct sockaddr_dlx sdlx;
288ef8d499eSDavid van Moolenbroek size_t nlen, alen;
289ef8d499eSDavid van Moolenbroek
290ef8d499eSDavid van Moolenbroek if (addr_len < offsetof(struct sockaddr_dlx, sdlx_data))
291ef8d499eSDavid van Moolenbroek return EINVAL;
292ef8d499eSDavid van Moolenbroek
293ef8d499eSDavid van Moolenbroek /*
294ef8d499eSDavid van Moolenbroek * We cannot prevent callers from passing in massively oversized
295ef8d499eSDavid van Moolenbroek * sockaddr_dl structure. However, we insist that all the actual data
296ef8d499eSDavid van Moolenbroek * be contained within the size of our sockaddr_dlx version.
297ef8d499eSDavid van Moolenbroek */
298ef8d499eSDavid van Moolenbroek if (addr_len > sizeof(sdlx))
299ef8d499eSDavid van Moolenbroek addr_len = sizeof(sdlx);
300ef8d499eSDavid van Moolenbroek
301ef8d499eSDavid van Moolenbroek memcpy(&sdlx, addr, addr_len);
302ef8d499eSDavid van Moolenbroek
303ef8d499eSDavid van Moolenbroek if (sdlx.sdlx_family != AF_LINK)
304ef8d499eSDavid van Moolenbroek return EAFNOSUPPORT;
305ef8d499eSDavid van Moolenbroek
306ef8d499eSDavid van Moolenbroek /* Address selectors are not currently supported. */
307ef8d499eSDavid van Moolenbroek if (sdlx.sdlx_slen != 0)
308ef8d499eSDavid van Moolenbroek return EINVAL;
309ef8d499eSDavid van Moolenbroek
310ef8d499eSDavid van Moolenbroek nlen = (size_t)sdlx.sdlx_nlen;
311ef8d499eSDavid van Moolenbroek alen = (size_t)sdlx.sdlx_alen;
312ef8d499eSDavid van Moolenbroek
313ef8d499eSDavid van Moolenbroek /* The nlen and alen fields are 8-bit, so no risks of overflow here. */
314ef8d499eSDavid van Moolenbroek if (addr_len < offsetof(struct sockaddr_dlx, sdlx_data) + nlen + alen)
315ef8d499eSDavid van Moolenbroek return EINVAL;
316ef8d499eSDavid van Moolenbroek
317ef8d499eSDavid van Moolenbroek /*
318ef8d499eSDavid van Moolenbroek * Copy out the name, truncating it if needed. The name in the
319ef8d499eSDavid van Moolenbroek * sockaddr is not null terminated, so we have to do that. If the
320ef8d499eSDavid van Moolenbroek * sockaddr has no name, copy out an empty name.
321ef8d499eSDavid van Moolenbroek */
322ef8d499eSDavid van Moolenbroek if (name != NULL) {
323ef8d499eSDavid van Moolenbroek assert(name_max > 0);
324ef8d499eSDavid van Moolenbroek
325ef8d499eSDavid van Moolenbroek if (name_max > nlen + 1)
326ef8d499eSDavid van Moolenbroek name_max = nlen + 1;
327ef8d499eSDavid van Moolenbroek
328ef8d499eSDavid van Moolenbroek memcpy(name, sdlx.sdlx_data, name_max - 1);
329ef8d499eSDavid van Moolenbroek name[name_max - 1] = '\0';
330ef8d499eSDavid van Moolenbroek }
331ef8d499eSDavid van Moolenbroek
332ef8d499eSDavid van Moolenbroek /*
333ef8d499eSDavid van Moolenbroek * Copy over the hardware address. For simplicity, we require that the
334ef8d499eSDavid van Moolenbroek * caller specify the exact hardware address length.
335ef8d499eSDavid van Moolenbroek */
336ef8d499eSDavid van Moolenbroek if (hwaddr != NULL) {
337ef8d499eSDavid van Moolenbroek if (alen != hwaddr_len)
338ef8d499eSDavid van Moolenbroek return EINVAL;
339ef8d499eSDavid van Moolenbroek
340ef8d499eSDavid van Moolenbroek memcpy(hwaddr, sdlx.sdlx_data + nlen, hwaddr_len);
341ef8d499eSDavid van Moolenbroek }
342ef8d499eSDavid van Moolenbroek
343ef8d499eSDavid van Moolenbroek return OK;
344ef8d499eSDavid van Moolenbroek }
345ef8d499eSDavid van Moolenbroek
346ef8d499eSDavid van Moolenbroek /*
347ef8d499eSDavid van Moolenbroek * Store a link-layer sockaddr structure (sockaddr_dl), to be copied to
348ef8d499eSDavid van Moolenbroek * userland. The result is stored in the buffer pointed to by 'addr'. Before
349ef8d499eSDavid van Moolenbroek * the call, 'addr_len' must be set to the size of this buffer. This is an
350ef8d499eSDavid van Moolenbroek * internal check to prevent buffer overflows, and must not be used to validate
351ef8d499eSDavid van Moolenbroek * input, since a mismatch will trigger a panic. After the call, 'addr_len'
352ef8d499eSDavid van Moolenbroek * will be set to the size of the resulting structure. The given interface
353ef8d499eSDavid van Moolenbroek * index 'ifindex' and (IFT_) interface type 'type' will always be stored in
354ef8d499eSDavid van Moolenbroek * the resulting structure. If 'name' is not NULL, it must be a null-
355ef8d499eSDavid van Moolenbroek * terminated interface name string which will be included in the structure.
356ef8d499eSDavid van Moolenbroek * If 'hwaddr' is not NULL, it must be a hardware address of length
357ef8d499eSDavid van Moolenbroek * 'hwaddr_len', which will also be included in the structure.
358ef8d499eSDavid van Moolenbroek */
359ef8d499eSDavid van Moolenbroek void
addr_put_link(struct sockaddr * addr,socklen_t * addr_len,uint32_t ifindex,uint32_t type,const char * name,const uint8_t * hwaddr,size_t hwaddr_len)360ef8d499eSDavid van Moolenbroek addr_put_link(struct sockaddr * addr, socklen_t * addr_len, uint32_t ifindex,
361ef8d499eSDavid van Moolenbroek uint32_t type, const char * name, const uint8_t * hwaddr,
362ef8d499eSDavid van Moolenbroek size_t hwaddr_len)
363ef8d499eSDavid van Moolenbroek {
364ef8d499eSDavid van Moolenbroek struct sockaddr_dlx sdlx;
365ef8d499eSDavid van Moolenbroek size_t name_len;
366ef8d499eSDavid van Moolenbroek socklen_t len;
367ef8d499eSDavid van Moolenbroek
368ef8d499eSDavid van Moolenbroek name_len = (name != NULL) ? strlen(name) : 0;
369ef8d499eSDavid van Moolenbroek
370ef8d499eSDavid van Moolenbroek if (hwaddr == NULL)
371ef8d499eSDavid van Moolenbroek hwaddr_len = 0;
372ef8d499eSDavid van Moolenbroek
373ef8d499eSDavid van Moolenbroek assert(name_len < IFNAMSIZ);
374ef8d499eSDavid van Moolenbroek assert(hwaddr_len <= NETIF_MAX_HWADDR_LEN);
375ef8d499eSDavid van Moolenbroek
376ef8d499eSDavid van Moolenbroek len = offsetof(struct sockaddr_dlx, sdlx_data) + name_len + hwaddr_len;
377ef8d499eSDavid van Moolenbroek
378ef8d499eSDavid van Moolenbroek if (*addr_len < len)
379ef8d499eSDavid van Moolenbroek panic("provided address buffer too small");
380ef8d499eSDavid van Moolenbroek
381ef8d499eSDavid van Moolenbroek memset(&sdlx, 0, sizeof(sdlx));
382ef8d499eSDavid van Moolenbroek sdlx.sdlx_len = len;
383ef8d499eSDavid van Moolenbroek sdlx.sdlx_family = AF_LINK;
384ef8d499eSDavid van Moolenbroek sdlx.sdlx_index = ifindex;
385ef8d499eSDavid van Moolenbroek sdlx.sdlx_type = type;
386ef8d499eSDavid van Moolenbroek sdlx.sdlx_nlen = name_len;
387ef8d499eSDavid van Moolenbroek sdlx.sdlx_alen = hwaddr_len;
388ef8d499eSDavid van Moolenbroek if (name_len > 0)
389ef8d499eSDavid van Moolenbroek memcpy(sdlx.sdlx_data, name, name_len);
390ef8d499eSDavid van Moolenbroek if (hwaddr_len > 0)
391ef8d499eSDavid van Moolenbroek memcpy(sdlx.sdlx_data + name_len, hwaddr, hwaddr_len);
392ef8d499eSDavid van Moolenbroek
393ef8d499eSDavid van Moolenbroek memcpy(addr, &sdlx, len);
394ef8d499eSDavid van Moolenbroek *addr_len = len;
395ef8d499eSDavid van Moolenbroek }
396ef8d499eSDavid van Moolenbroek
397ef8d499eSDavid van Moolenbroek /*
398ef8d499eSDavid van Moolenbroek * Convert an IPv4 or IPv6 netmask, given as sockaddr structure 'addr', to a
399ef8d499eSDavid van Moolenbroek * prefix length. The length of the sockaddr structure is given as 'addr_len'.
400ef8d499eSDavid van Moolenbroek * For consistency with addr_get_inet(), the expected address type is given as
401ef8d499eSDavid van Moolenbroek * 'type', and must be either IPADDR_TYPE_V4 or IPADDR_TYPE_V6. On success,
402ef8d499eSDavid van Moolenbroek * return OK with the number of set prefix bits returned in 'prefix', and
403ef8d499eSDavid van Moolenbroek * optionally with a lwIP representation of the netmask stored in 'ipaddr' (if
404ef8d499eSDavid van Moolenbroek * not NULL). On failure, return an appropriate negative error code. Note
405ef8d499eSDavid van Moolenbroek * that this function does not support compressed IPv4 network masks; such
406ef8d499eSDavid van Moolenbroek * addresses must be expanded before a call to this function.
407ef8d499eSDavid van Moolenbroek */
408ef8d499eSDavid van Moolenbroek int
addr_get_netmask(const struct sockaddr * addr,socklen_t addr_len,uint8_t type,unsigned int * prefix,ip_addr_t * ipaddr)409ef8d499eSDavid van Moolenbroek addr_get_netmask(const struct sockaddr * addr, socklen_t addr_len,
410ef8d499eSDavid van Moolenbroek uint8_t type, unsigned int * prefix, ip_addr_t * ipaddr)
411ef8d499eSDavid van Moolenbroek {
412ef8d499eSDavid van Moolenbroek struct sockaddr_in sin;
413ef8d499eSDavid van Moolenbroek struct sockaddr_in6 sin6;
414ef8d499eSDavid van Moolenbroek unsigned int byte, bit;
415ef8d499eSDavid van Moolenbroek uint32_t val;
416ef8d499eSDavid van Moolenbroek
417ef8d499eSDavid van Moolenbroek switch (type) {
418ef8d499eSDavid van Moolenbroek case IPADDR_TYPE_V4:
419ef8d499eSDavid van Moolenbroek if (addr_len != sizeof(sin))
420ef8d499eSDavid van Moolenbroek return EINVAL;
421ef8d499eSDavid van Moolenbroek
422ef8d499eSDavid van Moolenbroek memcpy(&sin, addr, sizeof(sin));
423ef8d499eSDavid van Moolenbroek
424ef8d499eSDavid van Moolenbroek if (sin.sin_family != AF_INET)
425ef8d499eSDavid van Moolenbroek return EAFNOSUPPORT;
426ef8d499eSDavid van Moolenbroek
427ef8d499eSDavid van Moolenbroek val = ntohl(sin.sin_addr.s_addr);
428ef8d499eSDavid van Moolenbroek
429ef8d499eSDavid van Moolenbroek /* Find the first zero bit. */
430ef8d499eSDavid van Moolenbroek for (bit = 0; bit < IP4_BITS; bit++)
431ef8d499eSDavid van Moolenbroek if (!(val & (1 << (IP4_BITS - bit - 1))))
432ef8d499eSDavid van Moolenbroek break;
433ef8d499eSDavid van Moolenbroek
434ef8d499eSDavid van Moolenbroek *prefix = bit;
435ef8d499eSDavid van Moolenbroek
436ef8d499eSDavid van Moolenbroek /* All bits after the first zero bit must also be zero. */
437ef8d499eSDavid van Moolenbroek if (bit < IP4_BITS &&
438ef8d499eSDavid van Moolenbroek (val & ((1 << (IP4_BITS - bit - 1)) - 1)))
439ef8d499eSDavid van Moolenbroek return EINVAL;
440ef8d499eSDavid van Moolenbroek
441ef8d499eSDavid van Moolenbroek if (ipaddr != NULL)
442ef8d499eSDavid van Moolenbroek ip_addr_set_ip4_u32(ipaddr, sin.sin_addr.s_addr);
443ef8d499eSDavid van Moolenbroek
444ef8d499eSDavid van Moolenbroek return OK;
445ef8d499eSDavid van Moolenbroek
446ef8d499eSDavid van Moolenbroek case IPADDR_TYPE_V6:
447ef8d499eSDavid van Moolenbroek if (addr_len != sizeof(sin6))
448ef8d499eSDavid van Moolenbroek return EINVAL;
449ef8d499eSDavid van Moolenbroek
450ef8d499eSDavid van Moolenbroek memcpy(&sin6, addr, sizeof(sin6));
451ef8d499eSDavid van Moolenbroek
452ef8d499eSDavid van Moolenbroek if (sin6.sin6_family != AF_INET6)
453ef8d499eSDavid van Moolenbroek return EAFNOSUPPORT;
454ef8d499eSDavid van Moolenbroek
455ef8d499eSDavid van Moolenbroek /* Find the first zero bit. */
456ef8d499eSDavid van Moolenbroek for (byte = 0; byte < __arraycount(sin6.sin6_addr.s6_addr);
457ef8d499eSDavid van Moolenbroek byte++)
458ef8d499eSDavid van Moolenbroek if (sin6.sin6_addr.s6_addr[byte] != 0xff)
459ef8d499eSDavid van Moolenbroek break;
460ef8d499eSDavid van Moolenbroek
461ef8d499eSDavid van Moolenbroek /* If all bits are set, there is nothing more to do. */
462ef8d499eSDavid van Moolenbroek if (byte == __arraycount(sin6.sin6_addr.s6_addr)) {
463ef8d499eSDavid van Moolenbroek *prefix = __arraycount(sin6.sin6_addr.s6_addr) * NBBY;
464ef8d499eSDavid van Moolenbroek
465ef8d499eSDavid van Moolenbroek return OK;
466ef8d499eSDavid van Moolenbroek }
467ef8d499eSDavid van Moolenbroek
468ef8d499eSDavid van Moolenbroek for (bit = 0; bit < NBBY; bit++)
469ef8d499eSDavid van Moolenbroek if (!(sin6.sin6_addr.s6_addr[byte] &
470ef8d499eSDavid van Moolenbroek (1 << (NBBY - bit - 1))))
471ef8d499eSDavid van Moolenbroek break;
472ef8d499eSDavid van Moolenbroek
473ef8d499eSDavid van Moolenbroek *prefix = byte * NBBY + bit;
474ef8d499eSDavid van Moolenbroek
475ef8d499eSDavid van Moolenbroek /* All bits after the first zero bit must also be zero. */
476ef8d499eSDavid van Moolenbroek if (bit < NBBY && (sin6.sin6_addr.s6_addr[byte] &
477ef8d499eSDavid van Moolenbroek ((1 << (NBBY - bit - 1)) - 1)))
478ef8d499eSDavid van Moolenbroek return EINVAL;
479ef8d499eSDavid van Moolenbroek
480ef8d499eSDavid van Moolenbroek for (byte++; byte < __arraycount(sin6.sin6_addr.s6_addr);
481ef8d499eSDavid van Moolenbroek byte++)
482ef8d499eSDavid van Moolenbroek if (sin6.sin6_addr.s6_addr[byte] != 0)
483ef8d499eSDavid van Moolenbroek return EINVAL;
484ef8d499eSDavid van Moolenbroek
485ef8d499eSDavid van Moolenbroek if (ipaddr != NULL) {
486ef8d499eSDavid van Moolenbroek ip_addr_set_zero_ip6(ipaddr);
487ef8d499eSDavid van Moolenbroek
488ef8d499eSDavid van Moolenbroek memcpy(ip_2_ip6(ipaddr)->addr, &sin6.sin6_addr,
489ef8d499eSDavid van Moolenbroek sizeof(ip_2_ip6(ipaddr)->addr));
490ef8d499eSDavid van Moolenbroek }
491ef8d499eSDavid van Moolenbroek
492ef8d499eSDavid van Moolenbroek return OK;
493ef8d499eSDavid van Moolenbroek
494ef8d499eSDavid van Moolenbroek default:
495ef8d499eSDavid van Moolenbroek panic("unknown IP address type: %u", type);
496ef8d499eSDavid van Moolenbroek }
497ef8d499eSDavid van Moolenbroek }
498ef8d499eSDavid van Moolenbroek
499ef8d499eSDavid van Moolenbroek /*
500ef8d499eSDavid van Moolenbroek * Generate a raw network mask based on the given prefix length.
501ef8d499eSDavid van Moolenbroek */
502ef8d499eSDavid van Moolenbroek void
addr_make_netmask(uint8_t * addr,socklen_t addr_len,unsigned int prefix)503ef8d499eSDavid van Moolenbroek addr_make_netmask(uint8_t * addr, socklen_t addr_len, unsigned int prefix)
504ef8d499eSDavid van Moolenbroek {
505ef8d499eSDavid van Moolenbroek unsigned int byte, bit;
506ef8d499eSDavid van Moolenbroek
507ef8d499eSDavid van Moolenbroek byte = prefix / NBBY;
508ef8d499eSDavid van Moolenbroek bit = prefix % NBBY;
509ef8d499eSDavid van Moolenbroek
510ef8d499eSDavid van Moolenbroek assert(byte + !!bit <= addr_len);
511ef8d499eSDavid van Moolenbroek
512ef8d499eSDavid van Moolenbroek if (byte > 0)
513ef8d499eSDavid van Moolenbroek memset(addr, 0xff, byte);
514ef8d499eSDavid van Moolenbroek if (bit != 0)
515ef8d499eSDavid van Moolenbroek addr[byte++] = (uint8_t)(0xff << (NBBY - bit));
516ef8d499eSDavid van Moolenbroek if (byte < addr_len)
517ef8d499eSDavid van Moolenbroek memset(&addr[byte], 0, addr_len - byte);
518ef8d499eSDavid van Moolenbroek }
519ef8d499eSDavid van Moolenbroek
520ef8d499eSDavid van Moolenbroek /*
521ef8d499eSDavid van Moolenbroek * Store a network mask as a sockaddr structure, in 'addr'. Before the call,
522ef8d499eSDavid van Moolenbroek * 'addr_len' must be set to the memory size of 'addr'. The address type is
523ef8d499eSDavid van Moolenbroek * given as 'type', and must be either IPADDR_TYPE_V4 or IPADDR_TYPE_V6. The
524ef8d499eSDavid van Moolenbroek * prefix length from which to generate the network mask is given as 'prefix'.
525ef8d499eSDavid van Moolenbroek * Upon return, 'addr_len' is set to the size of the resulting sockaddr
526ef8d499eSDavid van Moolenbroek * structure.
527ef8d499eSDavid van Moolenbroek */
528ef8d499eSDavid van Moolenbroek void
addr_put_netmask(struct sockaddr * addr,socklen_t * addr_len,uint8_t type,unsigned int prefix)529ef8d499eSDavid van Moolenbroek addr_put_netmask(struct sockaddr * addr, socklen_t * addr_len, uint8_t type,
530ef8d499eSDavid van Moolenbroek unsigned int prefix)
531ef8d499eSDavid van Moolenbroek {
532ef8d499eSDavid van Moolenbroek struct sockaddr_in sin;
533ef8d499eSDavid van Moolenbroek struct sockaddr_in6 sin6;
534ef8d499eSDavid van Moolenbroek
535ef8d499eSDavid van Moolenbroek switch (type) {
536ef8d499eSDavid van Moolenbroek case IPADDR_TYPE_V4:
537ef8d499eSDavid van Moolenbroek if (*addr_len < sizeof(sin))
538ef8d499eSDavid van Moolenbroek panic("provided address buffer too small");
539ef8d499eSDavid van Moolenbroek
540ef8d499eSDavid van Moolenbroek assert(prefix <= IP4_BITS);
541ef8d499eSDavid van Moolenbroek
542ef8d499eSDavid van Moolenbroek memset(&sin, 0, sizeof(sin));
543ef8d499eSDavid van Moolenbroek sin.sin_len = sizeof(sin);
544ef8d499eSDavid van Moolenbroek sin.sin_family = AF_INET;
545ef8d499eSDavid van Moolenbroek
546ef8d499eSDavid van Moolenbroek addr_make_netmask((uint8_t *)&sin.sin_addr.s_addr,
547ef8d499eSDavid van Moolenbroek sizeof(sin.sin_addr.s_addr), prefix);
548ef8d499eSDavid van Moolenbroek
549ef8d499eSDavid van Moolenbroek memcpy(addr, &sin, sizeof(sin));
550ef8d499eSDavid van Moolenbroek *addr_len = sizeof(sin);
551ef8d499eSDavid van Moolenbroek
552ef8d499eSDavid van Moolenbroek break;
553ef8d499eSDavid van Moolenbroek
554ef8d499eSDavid van Moolenbroek case IPADDR_TYPE_V6:
555ef8d499eSDavid van Moolenbroek if (*addr_len < sizeof(sin6))
556ef8d499eSDavid van Moolenbroek panic("provided address buffer too small");
557ef8d499eSDavid van Moolenbroek
558ef8d499eSDavid van Moolenbroek assert(prefix <= IP6_BITS);
559ef8d499eSDavid van Moolenbroek
560ef8d499eSDavid van Moolenbroek memset(&sin6, 0, sizeof(sin6));
561ef8d499eSDavid van Moolenbroek sin6.sin6_len = sizeof(sin6);
562ef8d499eSDavid van Moolenbroek sin6.sin6_family = AF_INET6;
563ef8d499eSDavid van Moolenbroek
564ef8d499eSDavid van Moolenbroek addr_make_netmask(sin6.sin6_addr.s6_addr,
565ef8d499eSDavid van Moolenbroek sizeof(sin6.sin6_addr.s6_addr), prefix);
566ef8d499eSDavid van Moolenbroek
567ef8d499eSDavid van Moolenbroek memcpy(addr, &sin6, sizeof(sin6));
568ef8d499eSDavid van Moolenbroek *addr_len = sizeof(sin6);
569ef8d499eSDavid van Moolenbroek
570ef8d499eSDavid van Moolenbroek break;
571ef8d499eSDavid van Moolenbroek
572ef8d499eSDavid van Moolenbroek default:
573ef8d499eSDavid van Moolenbroek panic("unknown IP address type: %u", type);
574ef8d499eSDavid van Moolenbroek }
575ef8d499eSDavid van Moolenbroek }
576ef8d499eSDavid van Moolenbroek
577ef8d499eSDavid van Moolenbroek /*
578ef8d499eSDavid van Moolenbroek * Normalize the given address in 'src' to the given number of prefix bits,
579ef8d499eSDavid van Moolenbroek * setting all other bits to zero. Return the result in 'dst'.
580ef8d499eSDavid van Moolenbroek */
581ef8d499eSDavid van Moolenbroek void
addr_normalize(ip_addr_t * dst,const ip_addr_t * src,unsigned int prefix)582ef8d499eSDavid van Moolenbroek addr_normalize(ip_addr_t * dst, const ip_addr_t * src, unsigned int prefix)
583ef8d499eSDavid van Moolenbroek {
584*03ac74edSLionel Sambuc #if !defined(NDEBUG)
585*03ac74edSLionel Sambuc unsigned int addr_len;
586*03ac74edSLionel Sambuc #endif /* !defined(NDEBUG) */
587*03ac74edSLionel Sambuc unsigned int byte, bit;
588ef8d499eSDavid van Moolenbroek const uint8_t *srcaddr;
589ef8d499eSDavid van Moolenbroek uint8_t type, *dstaddr;
590ef8d499eSDavid van Moolenbroek
591ef8d499eSDavid van Moolenbroek type = IP_GET_TYPE(src);
592ef8d499eSDavid van Moolenbroek
593ef8d499eSDavid van Moolenbroek memset(dst, 0, sizeof(*dst));
594ef8d499eSDavid van Moolenbroek IP_SET_TYPE(dst, type);
595ef8d499eSDavid van Moolenbroek
596ef8d499eSDavid van Moolenbroek switch (type) {
597ef8d499eSDavid van Moolenbroek case IPADDR_TYPE_V4:
598ef8d499eSDavid van Moolenbroek srcaddr = (const uint8_t *)&ip_2_ip4(src)->addr;
599ef8d499eSDavid van Moolenbroek dstaddr = (uint8_t *)&ip_2_ip4(dst)->addr;
600*03ac74edSLionel Sambuc #if !defined(NDEBUG)
601ef8d499eSDavid van Moolenbroek addr_len = sizeof(ip_2_ip4(src)->addr);
602*03ac74edSLionel Sambuc #endif /* !defined(NDEBUG) */
603ef8d499eSDavid van Moolenbroek
604ef8d499eSDavid van Moolenbroek break;
605ef8d499eSDavid van Moolenbroek
606ef8d499eSDavid van Moolenbroek case IPADDR_TYPE_V6:
607ef8d499eSDavid van Moolenbroek ip6_addr_set_zone(ip_2_ip6(dst), ip6_addr_zone(ip_2_ip6(src)));
608ef8d499eSDavid van Moolenbroek
609ef8d499eSDavid van Moolenbroek srcaddr = (const uint8_t *)&ip_2_ip6(src)->addr;
610ef8d499eSDavid van Moolenbroek dstaddr = (uint8_t *)&ip_2_ip6(dst)->addr;
611*03ac74edSLionel Sambuc #if !defined(NDEBUG)
612ef8d499eSDavid van Moolenbroek addr_len = sizeof(ip_2_ip6(src)->addr);
613*03ac74edSLionel Sambuc #endif /* !defined(NDEBUG) */
614ef8d499eSDavid van Moolenbroek
615ef8d499eSDavid van Moolenbroek break;
616ef8d499eSDavid van Moolenbroek
617ef8d499eSDavid van Moolenbroek default:
618ef8d499eSDavid van Moolenbroek panic("unknown IP address type: %u", type);
619ef8d499eSDavid van Moolenbroek }
620ef8d499eSDavid van Moolenbroek
621ef8d499eSDavid van Moolenbroek byte = prefix / NBBY;
622ef8d499eSDavid van Moolenbroek bit = prefix % NBBY;
623ef8d499eSDavid van Moolenbroek
624ef8d499eSDavid van Moolenbroek assert(byte + !!bit <= addr_len);
625ef8d499eSDavid van Moolenbroek
626ef8d499eSDavid van Moolenbroek if (byte > 0)
627ef8d499eSDavid van Moolenbroek memcpy(dstaddr, srcaddr, byte);
628ef8d499eSDavid van Moolenbroek if (bit != 0) {
629ef8d499eSDavid van Moolenbroek dstaddr[byte] =
630ef8d499eSDavid van Moolenbroek srcaddr[byte] & (uint8_t)(0xff << (NBBY - bit));
631ef8d499eSDavid van Moolenbroek byte++;
632ef8d499eSDavid van Moolenbroek }
633ef8d499eSDavid van Moolenbroek }
634ef8d499eSDavid van Moolenbroek
635ef8d499eSDavid van Moolenbroek /*
636ef8d499eSDavid van Moolenbroek * Return the number of common bits between the given two addresses, up to the
637ef8d499eSDavid van Moolenbroek * given maximum. Thus, return a value between 0 and 'max' inclusive.
638ef8d499eSDavid van Moolenbroek */
639ef8d499eSDavid van Moolenbroek unsigned int
addr_get_common_bits(const ip_addr_t * ipaddr1,const ip_addr_t * ipaddr2,unsigned int max)640ef8d499eSDavid van Moolenbroek addr_get_common_bits(const ip_addr_t * ipaddr1, const ip_addr_t * ipaddr2,
641ef8d499eSDavid van Moolenbroek unsigned int max)
642ef8d499eSDavid van Moolenbroek {
643ef8d499eSDavid van Moolenbroek unsigned int addr_len, prefix, bit;
644ef8d499eSDavid van Moolenbroek const uint8_t *addr1, *addr2;
645ef8d499eSDavid van Moolenbroek uint8_t byte;
646ef8d499eSDavid van Moolenbroek
647ef8d499eSDavid van Moolenbroek switch (IP_GET_TYPE(ipaddr1)) {
648ef8d499eSDavid van Moolenbroek case IPADDR_TYPE_V4:
649ef8d499eSDavid van Moolenbroek assert(IP_IS_V4(ipaddr2));
650ef8d499eSDavid van Moolenbroek
651ef8d499eSDavid van Moolenbroek addr1 = (const uint8_t *)&ip_2_ip4(ipaddr1)->addr;
652ef8d499eSDavid van Moolenbroek addr2 = (const uint8_t *)&ip_2_ip4(ipaddr2)->addr;
653ef8d499eSDavid van Moolenbroek addr_len = sizeof(ip_2_ip4(ipaddr1)->addr);
654ef8d499eSDavid van Moolenbroek
655ef8d499eSDavid van Moolenbroek break;
656ef8d499eSDavid van Moolenbroek
657ef8d499eSDavid van Moolenbroek case IPADDR_TYPE_V6:
658ef8d499eSDavid van Moolenbroek assert(IP_IS_V6(ipaddr2));
659ef8d499eSDavid van Moolenbroek
660ef8d499eSDavid van Moolenbroek addr1 = (const uint8_t *)&ip_2_ip6(ipaddr1)->addr;
661ef8d499eSDavid van Moolenbroek addr2 = (const uint8_t *)&ip_2_ip6(ipaddr2)->addr;
662ef8d499eSDavid van Moolenbroek addr_len = sizeof(ip_2_ip6(ipaddr1)->addr);
663ef8d499eSDavid van Moolenbroek
664ef8d499eSDavid van Moolenbroek break;
665ef8d499eSDavid van Moolenbroek
666ef8d499eSDavid van Moolenbroek default:
667ef8d499eSDavid van Moolenbroek panic("unknown IP address type: %u", IP_GET_TYPE(ipaddr1));
668ef8d499eSDavid van Moolenbroek }
669ef8d499eSDavid van Moolenbroek
670ef8d499eSDavid van Moolenbroek if (addr_len > max * NBBY)
671ef8d499eSDavid van Moolenbroek addr_len = max * NBBY;
672ef8d499eSDavid van Moolenbroek
673ef8d499eSDavid van Moolenbroek prefix = 0;
674ef8d499eSDavid van Moolenbroek
675ef8d499eSDavid van Moolenbroek for (prefix = 0; addr_len > 0; addr1++, addr2++, prefix += NBBY) {
676ef8d499eSDavid van Moolenbroek if ((byte = (*addr1 ^ *addr2)) != 0) {
677ef8d499eSDavid van Moolenbroek /* TODO: see if we want a lookup table for this. */
678ef8d499eSDavid van Moolenbroek for (bit = 0; bit < NBBY; bit++, prefix++)
679ef8d499eSDavid van Moolenbroek if (byte & (1 << (NBBY - bit - 1)))
680ef8d499eSDavid van Moolenbroek break;
681ef8d499eSDavid van Moolenbroek break;
682ef8d499eSDavid van Moolenbroek }
683ef8d499eSDavid van Moolenbroek }
684ef8d499eSDavid van Moolenbroek
685ef8d499eSDavid van Moolenbroek if (prefix > max)
686ef8d499eSDavid van Moolenbroek prefix = max;
687ef8d499eSDavid van Moolenbroek
688ef8d499eSDavid van Moolenbroek return prefix;
689ef8d499eSDavid van Moolenbroek }
690ef8d499eSDavid van Moolenbroek
691ef8d499eSDavid van Moolenbroek /*
692ef8d499eSDavid van Moolenbroek * Convert the given IPv4 address to an IPv4-mapped IPv6 address.
693ef8d499eSDavid van Moolenbroek */
694ef8d499eSDavid van Moolenbroek void
addr_make_v4mapped_v6(ip_addr_t * dst,const ip4_addr_t * src)695ef8d499eSDavid van Moolenbroek addr_make_v4mapped_v6(ip_addr_t * dst, const ip4_addr_t * src)
696ef8d499eSDavid van Moolenbroek {
697ef8d499eSDavid van Moolenbroek
698ef8d499eSDavid van Moolenbroek IP_ADDR6(dst, 0, 0, PP_HTONL(0x0000ffffUL), ip4_addr_get_u32(src));
699ef8d499eSDavid van Moolenbroek }
700