1 /* $NetBSD: link_proto.c,v 1.4 2008/05/13 18:09:22 dyoung Exp $ */
4 * Copyright (c) 1982, 1986, 1993
5 * The Regents of the University of California. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * @(#)uipc_proto.c 8.2 (Berkeley) 2/14/95
34 #include <sys/cdefs.h>
35 __KERNEL_RCSID(0, "$NetBSD: link_proto.c,v 1.4 2008/05/13 18:09:22 dyoung Exp $");
37 #include <sys/param.h>
38 #include <sys/socket.h>
39 #include <sys/protosw.h>
40 #include <sys/domain.h>
43 #include <sys/socketvar.h>
46 #include <net/if_dl.h>
47 #include <net/raw_cb.h>
48 #include <net/route.h>
50 static int sockaddr_dl_cmp(const struct sockaddr
*, const struct sockaddr
*);
51 static int link_usrreq(struct socket
*, int, struct mbuf
*, struct mbuf
*,
52 struct mbuf
*, struct lwp
*);
53 static void link_init(void);
56 * Definitions of protocols supported in the link-layer domain.
59 DOMAIN_DEFINE(linkdomain
); /* forward define and add to link set */
61 const struct protosw linksw
[] = {
62 { .pr_type
= SOCK_DGRAM
,
63 .pr_domain
= &linkdomain
,
64 .pr_protocol
= 0, /* XXX */
65 .pr_flags
= PR_ATOMIC
|PR_ADDR
|PR_PURGEIF
,
69 .pr_usrreq
= link_usrreq
,
74 struct domain linkdomain
= {
75 .dom_family
= AF_LINK
,
77 .dom_externalize
= NULL
,
79 .dom_protosw
= linksw
,
80 .dom_protoswNPROTOSW
= &linksw
[__arraycount(linksw
)],
81 .dom_sockaddr_cmp
= sockaddr_dl_cmp
91 link_control(struct socket
*so
, unsigned long cmd
, void *data
,
92 struct ifnet
*ifp
, struct lwp
*l
)
95 bool isactive
, mkactive
;
96 struct if_laddrreq
*iflr
;
99 struct sockaddr_dl sdl
;
100 struct sockaddr_storage ss
;
103 const struct sockaddr_dl
*asdl
, *nsdl
;
111 if (iflr
->addr
.ss_family
!= AF_LINK
)
114 asdl
= satocsdl(sstocsa(&iflr
->addr
));
116 if (asdl
->sdl_alen
!= ifp
->if_addrlen
)
119 if (sockaddr_dl_init(&u
.sdl
, sizeof(u
.ss
), ifp
->if_index
,
120 ifp
->if_type
, ifp
->if_xname
, strlen(ifp
->if_xname
),
121 CLLADDR(asdl
), asdl
->sdl_alen
) == NULL
)
124 if ((iflr
->flags
& IFLR_PREFIX
) == 0)
126 else if (iflr
->prefixlen
!= NBBY
* ifp
->if_addrlen
)
127 return EINVAL
; /* XXX match with prefix */
133 IFADDR_FOREACH(ifa
, ifp
) {
134 if (sockaddr_cmp(&u
.sa
, ifa
->ifa_addr
) == 0)
140 if ((iflr
->flags
& IFLR_PREFIX
) == 0) {
141 IFADDR_FOREACH(ifa
, ifp
) {
142 if (ifa
->ifa_addr
->sa_family
== AF_LINK
)
147 error
= EADDRNOTAVAIL
;
151 if (ifa
== ifp
->if_dl
)
152 iflr
->flags
= IFLR_ACTIVE
;
156 if (ifa
== ifp
->if_hwdl
)
157 iflr
->flags
|= IFLR_FACTORY
;
159 sockaddr_copy(sstosa(&iflr
->addr
), sizeof(iflr
->addr
),
165 error
= EADDRNOTAVAIL
;
166 else if (ifa
== ifp
->if_dl
|| ifa
== ifp
->if_hwdl
)
169 /* TBD routing socket */
170 rt_newaddrmsg(RTM_DELETE
, ifa
, 0, NULL
);
171 ifa_remove(ifp
, ifa
);
177 else if ((ifa
= if_dl_create(ifp
, &nsdl
)) == NULL
) {
181 sockaddr_copy(ifa
->ifa_addr
,
182 ifa
->ifa_addr
->sa_len
, &u
.sa
);
183 ifa_insert(ifp
, ifa
);
184 rt_newaddrmsg(RTM_ADD
, ifa
, 0, NULL
);
187 mkactive
= (iflr
->flags
& IFLR_ACTIVE
) != 0;
188 isactive
= (ifa
== ifp
->if_dl
);
190 if (!isactive
&& mkactive
) {
191 if_activate_sadl(ifp
, ifa
, nsdl
);
197 if (error
!= ENETRESET
)
199 else if ((ifp
->if_flags
& IFF_RUNNING
) != 0)
200 return (*ifp
->if_init
)(ifp
);
209 link_usrreq(struct socket
*so
, int req
, struct mbuf
*m
, struct mbuf
*nam
,
210 struct mbuf
*control
, struct lwp
*l
)
220 return link_control(so
, (unsigned long)m
, nam
,
221 (struct ifnet
*)control
, l
);
227 /* Compare the field at byte offsets [fieldstart, fieldend) in
228 * two memory regions, [l, l + llen) and [r, r + llen).
231 submemcmp(const void *l
, const void *r
,
232 const uint_fast8_t llen
, const uint_fast8_t rlen
,
233 const uint_fast8_t fieldstart
, const uint_fast8_t fieldend
)
235 uint_fast8_t cmpend
, minlen
;
236 const uint8_t *lb
= l
, *rb
= r
;
239 minlen
= MIN(llen
, rlen
);
241 /* The field is missing from one region. The shorter region is the
244 if (fieldstart
>= minlen
)
247 /* Two empty, present fields are always equal. */
248 if (fieldstart
> fieldend
)
251 cmpend
= MIN(fieldend
, minlen
);
253 rc
= memcmp(&lb
[fieldstart
], &rb
[fieldstart
], cmpend
- fieldstart
);
257 /* If one or both fields are truncated, then the shorter is the lesser
260 if (minlen
< fieldend
)
262 /* Fields are full-length and equal. The fields are equal. */
267 sockaddr_dl_measure(uint8_t namelen
, uint8_t addrlen
)
269 return offsetof(struct sockaddr_dl
, sdl_data
[namelen
+ addrlen
]);
273 sockaddr_dl_alloc(uint16_t ifindex
, uint8_t type
,
274 const void *name
, uint8_t namelen
, const void *addr
, uint8_t addrlen
,
280 len
= sockaddr_dl_measure(namelen
, addrlen
);
281 sa
= sockaddr_alloc(AF_LINK
, len
, flags
);
286 if (sockaddr_dl_init(satosdl(sa
), len
, ifindex
, type
, name
, namelen
,
287 addr
, addrlen
) == NULL
) {
296 sockaddr_dl_init(struct sockaddr_dl
*sdl
, socklen_t socklen
, uint16_t ifindex
,
297 uint8_t type
, const void *name
, uint8_t namelen
, const void *addr
,
302 sdl
->sdl_family
= AF_LINK
;
304 len
= sockaddr_dl_measure(namelen
, addrlen
);
306 sdl
->sdl_len
= socklen
;
308 printf("%s: too long: %" PRIu8
" > %" PRIu8
"\n", __func__
, len
,
314 sdl
->sdl_index
= ifindex
;
315 sdl
->sdl_type
= type
;
316 memset(&sdl
->sdl_data
[0], 0, namelen
+ addrlen
);
318 memcpy(&sdl
->sdl_data
[0], name
, namelen
);
319 sdl
->sdl_nlen
= namelen
;
323 memcpy(&sdl
->sdl_data
[sdl
->sdl_nlen
], addr
, addrlen
);
324 sdl
->sdl_alen
= addrlen
;
331 sockaddr_dl_cmp(const struct sockaddr
*sa1
, const struct sockaddr
*sa2
)
334 const uint_fast8_t indexofs
= offsetof(struct sockaddr_dl
, sdl_index
);
335 const uint_fast8_t nlenofs
= offsetof(struct sockaddr_dl
, sdl_nlen
);
336 uint_fast8_t dataofs
= offsetof(struct sockaddr_dl
, sdl_data
[0]);
337 const struct sockaddr_dl
*sdl1
, *sdl2
;
339 sdl1
= satocsdl(sa1
);
340 sdl2
= satocsdl(sa2
);
342 rc
= submemcmp(sdl1
, sdl2
, sdl1
->sdl_len
, sdl2
->sdl_len
,
348 rc
= submemcmp(sdl1
, sdl2
, sdl1
->sdl_len
, sdl2
->sdl_len
,
349 dataofs
, dataofs
+ MIN(sdl1
->sdl_nlen
, sdl2
->sdl_nlen
));
354 if (sdl1
->sdl_nlen
!= sdl2
->sdl_nlen
)
355 return sdl1
->sdl_nlen
- sdl2
->sdl_nlen
;
357 dataofs
+= sdl1
->sdl_nlen
;
359 rc
= submemcmp(sdl1
, sdl2
, sdl1
->sdl_len
, sdl2
->sdl_len
,
360 dataofs
, dataofs
+ MIN(sdl1
->sdl_alen
, sdl2
->sdl_alen
));
365 if (sdl1
->sdl_alen
!= sdl2
->sdl_alen
)
366 return sdl1
->sdl_alen
- sdl2
->sdl_alen
;
368 dataofs
+= sdl1
->sdl_alen
;
370 rc
= submemcmp(sdl1
, sdl2
, sdl1
->sdl_len
, sdl2
->sdl_len
,
371 dataofs
, dataofs
+ MIN(sdl1
->sdl_slen
, sdl2
->sdl_slen
));
373 if (sdl1
->sdl_slen
!= sdl2
->sdl_slen
)
374 return sdl1
->sdl_slen
- sdl2
->sdl_slen
;
376 return sdl1
->sdl_len
- sdl2
->sdl_len
;
380 sockaddr_dl_setaddr(struct sockaddr_dl
*sdl
, socklen_t socklen
,
381 const void *addr
, uint8_t addrlen
)
385 len
= sockaddr_dl_measure(sdl
->sdl_nlen
, addrlen
);
388 printf("%s: too long: %" PRIu8
" > %" PRIu8
"\n", __func__
, len
,
393 memcpy(&sdl
->sdl_data
[sdl
->sdl_nlen
], addr
, addrlen
);
394 sdl
->sdl_alen
= addrlen
;