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