xref: /haiku/src/libs/compat/freebsd_network/if.c (revision 6e927a5fc080cb934e7584454f472cacf4c3e361)
1 /*
2  * Copyright 2007, Hugo Santos. All Rights Reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *      Hugo Santos, hugosantos@gmail.com
7  *
8  * Some of this code is based on previous work by Marcus Overhagen.
9  */
10 
11 #include "device.h"
12 
13 #include <stdio.h>
14 #include <net/if_types.h>
15 #include <sys/sockio.h>
16 
17 #include <compat/sys/bus.h>
18 #include <compat/sys/kernel.h>
19 
20 #include <compat/net/if.h>
21 #include <compat/net/if_arp.h>
22 #include <compat/net/if_var.h>
23 #include <compat/sys/malloc.h>
24 
25 #include <compat/net/ethernet.h>
26 
27 struct ifnet *
28 if_alloc(u_char type)
29 {
30 	struct ifnet *ifp = _kernel_malloc(sizeof(struct ifnet), M_ZERO);
31 	if (ifp == NULL)
32 		return NULL;
33 
34 	if (type == IFT_ETHER) {
35 		ifp->if_l2com = _kernel_malloc(sizeof(struct arpcom), M_ZERO);
36 		if (ifp->if_l2com == NULL) {
37 			_kernel_free(ifp);
38 			return NULL;
39 		}
40 		IFP2AC(ifp)->ac_ifp = ifp;
41 	}
42 
43 	ifp->if_type = type;
44 	IF_ADDR_LOCK_INIT(ifp);
45 	return ifp;
46 }
47 
48 
49 void
50 if_free(struct ifnet *ifp)
51 {
52 	IF_ADDR_LOCK_DESTROY(ifp);
53 	if (ifp->if_type == IFT_ETHER)
54 		_kernel_free(ifp->if_l2com);
55 
56 	_kernel_free(ifp);
57 }
58 
59 
60 void
61 if_initname(struct ifnet *ifp, const char *name, int unit)
62 {
63 	int i;
64 
65 	dprintf("if_initname(%p, %s, %d)\n", ifp, name, unit);
66 
67 	if (name == NULL)
68 		panic("interface goes unamed");
69 
70 	ifp->if_dname = name;
71 	ifp->if_dunit = unit;
72 
73 	strlcpy(ifp->if_xname, name, sizeof(ifp->if_xname));
74 
75 	for (i = 0; gDevNameList[i] != NULL; i++) {
76 		if (strcmp(gDevNameList[i], name) == 0)
77 			break;
78 	}
79 
80 	if (gDevNameList[i] == NULL)
81 		panic("unknown interface");
82 
83 	ifp->if_dev = DEVNET(gDevices[i]);
84 	gDevices[i]->ifp = ifp;
85 }
86 
87 
88 void
89 ifq_init(struct ifqueue *ifq, const char *name)
90 {
91 	ifq->ifq_head = NULL;
92 	ifq->ifq_tail = NULL;
93 	ifq->ifq_len = 0;
94 	ifq->ifq_maxlen = IFQ_MAXLEN;
95 	ifq->ifq_drops = 0;
96 
97 	mtx_init(&ifq->ifq_mtx, name, NULL, MTX_DEF);
98 }
99 
100 
101 void
102 ifq_uninit(struct ifqueue *ifq)
103 {
104 	mtx_destroy(&ifq->ifq_mtx);
105 }
106 
107 
108 void
109 if_attach(struct ifnet *ifp)
110 {
111 	TAILQ_INIT(&ifp->if_addrhead);
112 	TAILQ_INIT(&ifp->if_prefixhead);
113 	TAILQ_INIT(&ifp->if_multiaddrs);
114 
115 	IF_ADDR_LOCK_INIT(ifp);
116 
117 	ifq_init((struct ifqueue *)&ifp->if_snd, ifp->if_xname);
118 }
119 
120 
121 void
122 if_detach(struct ifnet *ifp)
123 {
124 	IF_ADDR_LOCK_DESTROY(ifp);
125 	ifq_uninit((struct ifqueue *)&ifp->if_snd);
126 }
127 
128 
129 void
130 if_start(struct ifnet *ifp)
131 {
132 #ifdef IFF_NEEDSGIANT
133 	if (ifp->if_flags & IFF_NEEDSGIANT)
134 		panic("freebsd compat.: unsupported giant requirement");
135 #endif
136 	ifp->if_start(ifp);
137 }
138 
139 
140 int
141 if_printf(struct ifnet *ifp, const char *format, ...)
142 {
143 	char buf[256];
144 	va_list vl;
145 	va_start(vl, format);
146 	vsnprintf(buf, sizeof(buf), format, vl);
147 	va_end(vl);
148 
149 	dprintf("[%s] %s", ifp->if_xname, buf);
150 	return 0;
151 }
152 
153 
154 void
155 if_link_state_change(struct ifnet *ifp, int link_state)
156 {
157 	if (ifp->if_link_state == link_state)
158 		return;
159 
160 	ifp->if_link_state = link_state;
161 
162 	release_sem_etc(NETDEV(ifp->if_dev)->link_state_sem, 1,
163 		B_DO_NOT_RESCHEDULE);
164 }
165 
166 
167 static struct ifmultiaddr *
168 if_findmulti(struct ifnet *ifp, struct sockaddr *_address)
169 {
170 	struct sockaddr_dl *address = (struct sockaddr_dl *)_address;
171 	struct ifmultiaddr *ifma;
172 
173 	TAILQ_FOREACH (ifma, &ifp->if_multiaddrs, ifma_link) {
174 		if (memcmp(LLADDR(address),
175 				LLADDR((struct sockaddr_dl *)ifma->ifma_addr),
176 				ETHER_ADDR_LEN) == 0)
177 			return ifma;
178 	}
179 
180 	return NULL;
181 }
182 
183 
184 static struct ifmultiaddr *
185 _if_addmulti(struct ifnet *ifp, struct sockaddr *address)
186 {
187 	struct ifmultiaddr *addr = if_findmulti(ifp, address);
188 
189 	if (addr != NULL) {
190 		addr->ifma_refcount++;
191 		return addr;
192 	}
193 
194 	addr = (struct ifmultiaddr *)malloc(sizeof(struct ifmultiaddr));
195 	if (addr == NULL)
196 		return NULL;
197 
198 	addr->ifma_lladdr = NULL;
199 	addr->ifma_ifp = ifp;
200 	addr->ifma_protospec = NULL;
201 
202 	memcpy(&addr->ifma_addr_storage, address, sizeof(struct sockaddr_dl));
203 	addr->ifma_addr = (struct sockaddr *)&addr->ifma_addr_storage;
204 
205 	addr->ifma_refcount = 1;
206 
207 	TAILQ_INSERT_HEAD(&ifp->if_multiaddrs, addr, ifma_link);
208 
209 	return addr;
210 }
211 
212 
213 int
214 if_addmulti(struct ifnet *ifp, struct sockaddr *address,
215 	struct ifmultiaddr **out)
216 {
217 	struct ifmultiaddr *result;
218 	int refcount = 0;
219 
220 	IF_ADDR_LOCK(ifp);
221 	result = _if_addmulti(ifp, address);
222 	if (result)
223 		refcount = result->ifma_refcount;
224 	IF_ADDR_UNLOCK(ifp);
225 
226 	if (result == NULL)
227 		return ENOBUFS;
228 
229 	if (refcount == 1)
230 		ifp->if_ioctl(ifp, SIOCADDMULTI, NULL);
231 
232 	if (out)
233 		(*out) = result;
234 
235 	return 0;
236 }
237 
238 
239 static void
240 if_delete_multiaddr(struct ifnet *ifp, struct ifmultiaddr *ifma)
241 {
242 	TAILQ_REMOVE(&ifp->if_multiaddrs, ifma, ifma_link);
243 	free(ifma);
244 }
245 
246 
247 int
248 if_delmulti(struct ifnet *ifp, struct sockaddr *address)
249 {
250 	struct ifmultiaddr *addr;
251 	int deleted = 0;
252 
253 	IF_ADDR_LOCK(ifp);
254 	addr = if_findmulti(ifp, address);
255 	if (addr != NULL) {
256 		addr->ifma_refcount--;
257 		if (addr->ifma_refcount == 0) {
258 			if_delete_multiaddr(ifp, addr);
259 			deleted = 1;
260 		}
261 	}
262 	IF_ADDR_UNLOCK(ifp);
263 
264 	if (deleted)
265 		ifp->if_ioctl(ifp, SIOCDELMULTI, NULL);
266 
267 	return 0;
268 }
269 
270 
271 int
272 ether_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
273 	struct rtentry *rt0)
274 {
275 	int error = 0;
276 	IFQ_HANDOFF(ifp, m, error);
277 	return error;
278 }
279 
280 
281 static void
282 ether_input(struct ifnet *ifp, struct mbuf *m)
283 {
284 	device_t dev = ifp->if_dev;
285 
286 	IF_ENQUEUE(&NETDEV(dev)->receive_queue, m);
287 	release_sem_etc(NETDEV(dev)->receive_sem, 1, B_DO_NOT_RESCHEDULE);
288 }
289 
290 
291 void
292 ether_ifattach(struct ifnet *ifp, const uint8_t *mac_address)
293 {
294 	ifp->if_addrlen = ETHER_ADDR_LEN;
295 	ifp->if_hdrlen = ETHER_HDR_LEN;
296 	if_attach(ifp);
297 	ifp->if_mtu = ETHERMTU;
298 	ifp->if_output = ether_output;
299 	ifp->if_input = ether_input;
300 	ifp->if_resolvemulti = NULL; /* done in the stack */
301 
302 	ifp->if_lladdr.sdl_family = AF_LINK;
303 	memcpy(IF_LLADDR(ifp), mac_address, ETHER_ADDR_LEN);
304 
305 	ifp->if_init(ifp->if_softc);
306 }
307 
308 
309 void
310 ether_ifdetach(struct ifnet *ifp)
311 {
312 	if_detach(ifp);
313 }
314 
315 
316 int
317 ether_ioctl(struct ifnet *ifp, int command, caddr_t data)
318 {
319 	struct ifreq *ifr = (struct ifreq *)data;
320 
321 	switch (command) {
322 		case SIOCSIFMTU:
323 			if (ifr->ifr_mtu > ETHERMTU)
324 				return EINVAL;
325 			else
326 				;
327 				/* need to fix our ifreq to work with C... */
328 				/* ifp->ifr_mtu = ifr->ifr_mtu; */
329 			break;
330 
331 		default:
332 			return EINVAL;
333 	}
334 
335 	return 0;
336 }
337 
338