1 /*
2 * INET An implementation of the TCP/IP protocol suite for the LINUX
3 * operating system. INET is implemented using the BSD Socket
4 * interface as the means of communication with the user level.
5 *
6 * Generic frame diversion
7 *
8 * Version: @(#)eth.c 0.41 09/09/2000
9 *
10 * Authors:
11 * Benoit LOCHER: initial integration within the kernel with support for ethernet
12 * Dave Miller: improvement on the code (correctness, performance and source files)
13 *
14 */
15 #include <linux/types.h>
16 #include <linux/kernel.h>
17 #include <linux/sched.h>
18 #include <linux/string.h>
19 #include <linux/mm.h>
20 #include <linux/socket.h>
21 #include <linux/in.h>
22 #include <linux/inet.h>
23 #include <linux/ip.h>
24 #include <linux/udp.h>
25 #include <linux/netdevice.h>
26 #include <linux/etherdevice.h>
27 #include <linux/skbuff.h>
28 #include <linux/errno.h>
29 #include <linux/init.h>
30 #include <net/dst.h>
31 #include <net/arp.h>
32 #include <net/sock.h>
33 #include <net/ipv6.h>
34 #include <net/ip.h>
35 #include <asm/uaccess.h>
36 #include <asm/system.h>
37 #include <asm/checksum.h>
38 #include <linux/divert.h>
39 #include <linux/sockios.h>
40
41 const char sysctl_divert_version[32]="0.46"; /* Current version */
42
dv_init(void)43 int __init dv_init(void)
44 {
45 printk(KERN_INFO "NET4: Frame Diverter %s\n", sysctl_divert_version);
46 return 0;
47 }
48
49 /*
50 * Allocate a divert_blk for a device. This must be an ethernet nic.
51 */
alloc_divert_blk(struct net_device * dev)52 int alloc_divert_blk(struct net_device *dev)
53 {
54 int alloc_size = (sizeof(struct divert_blk) + 3) & ~3;
55
56 if (dev->type == ARPHRD_ETHER) {
57 printk(KERN_DEBUG "divert: allocating divert_blk for %s\n",
58 dev->name);
59
60 dev->divert = (struct divert_blk *)
61 kmalloc(alloc_size, GFP_KERNEL);
62 if (dev->divert == NULL) {
63 printk(KERN_DEBUG "divert: unable to allocate divert_blk for %s\n",
64 dev->name);
65 return -ENOMEM;
66 } else {
67 memset(dev->divert, 0, sizeof(struct divert_blk));
68 }
69 dev_hold(dev);
70 } else {
71 printk(KERN_DEBUG "divert: not allocating divert_blk for non-ethernet device %s\n",
72 dev->name);
73
74 dev->divert = NULL;
75 }
76 return 0;
77 }
78
79 /*
80 * Free a divert_blk allocated by the above function, if it was
81 * allocated on that device.
82 */
free_divert_blk(struct net_device * dev)83 void free_divert_blk(struct net_device *dev)
84 {
85 if (dev->divert) {
86 kfree(dev->divert);
87 dev->divert=NULL;
88 dev_put(dev);
89 printk(KERN_DEBUG "divert: freeing divert_blk for %s\n",
90 dev->name);
91 } else {
92 printk(KERN_DEBUG "divert: no divert_blk to free, %s not ethernet\n",
93 dev->name);
94 }
95 }
96
97 /*
98 * Adds a tcp/udp (source or dest) port to an array
99 */
add_port(u16 ports[],u16 port)100 int add_port(u16 ports[], u16 port)
101 {
102 int i;
103
104 if (port == 0)
105 return -EINVAL;
106
107 /* Storing directly in network format for performance,
108 * thanks Dave :)
109 */
110 port = htons(port);
111
112 for (i = 0; i < MAX_DIVERT_PORTS; i++) {
113 if (ports[i] == port)
114 return -EALREADY;
115 }
116
117 for (i = 0; i < MAX_DIVERT_PORTS; i++) {
118 if (ports[i] == 0) {
119 ports[i] = port;
120 return 0;
121 }
122 }
123
124 return -ENOBUFS;
125 }
126
127 /*
128 * Removes a port from an array tcp/udp (source or dest)
129 */
remove_port(u16 ports[],u16 port)130 int remove_port(u16 ports[], u16 port)
131 {
132 int i;
133
134 if (port == 0)
135 return -EINVAL;
136
137 /* Storing directly in network format for performance,
138 * thanks Dave !
139 */
140 port = htons(port);
141
142 for (i = 0; i < MAX_DIVERT_PORTS; i++) {
143 if (ports[i] == port) {
144 ports[i] = 0;
145 return 0;
146 }
147 }
148
149 return -EINVAL;
150 }
151
152 /* Some basic sanity checks on the arguments passed to divert_ioctl() */
check_args(struct divert_cf * div_cf,struct net_device ** dev)153 int check_args(struct divert_cf *div_cf, struct net_device **dev)
154 {
155 char devname[32];
156 int ret;
157
158 if (dev == NULL)
159 return -EFAULT;
160
161 /* GETVERSION: all other args are unused */
162 if (div_cf->cmd == DIVCMD_GETVERSION)
163 return 0;
164
165 /* Network device index should reasonably be between 0 and 1000 :) */
166 if (div_cf->dev_index < 0 || div_cf->dev_index > 1000)
167 return -EINVAL;
168
169 /* Let's try to find the ifname */
170 sprintf(devname, "eth%d", div_cf->dev_index);
171 *dev = dev_get_by_name(devname);
172
173 /* dev should NOT be null */
174 if (*dev == NULL)
175 return -EINVAL;
176
177 ret = 0;
178
179 /* user issuing the ioctl must be a super one :) */
180 if (!capable(CAP_SYS_ADMIN)) {
181 ret = -EPERM;
182 goto out;
183 }
184
185 /* Device must have a divert_blk member NOT null */
186 if ((*dev)->divert == NULL)
187 ret = -EINVAL;
188 out:
189 dev_put(*dev);
190 return ret;
191 }
192
193 /*
194 * control function of the diverter
195 */
196 #define DVDBG(a) \
197 printk(KERN_DEBUG "divert_ioctl() line %d %s\n", __LINE__, (a))
198
divert_ioctl(unsigned int cmd,struct divert_cf * arg)199 int divert_ioctl(unsigned int cmd, struct divert_cf *arg)
200 {
201 struct divert_cf div_cf;
202 struct divert_blk *div_blk;
203 struct net_device *dev;
204 int ret;
205
206 switch (cmd) {
207 case SIOCGIFDIVERT:
208 DVDBG("SIOCGIFDIVERT, copy_from_user");
209 if (copy_from_user(&div_cf, arg, sizeof(struct divert_cf)))
210 return -EFAULT;
211 DVDBG("before check_args");
212 ret = check_args(&div_cf, &dev);
213 if (ret)
214 return ret;
215 DVDBG("after checkargs");
216 div_blk = dev->divert;
217
218 DVDBG("befre switch()");
219 switch (div_cf.cmd) {
220 case DIVCMD_GETSTATUS:
221 /* Now, just give the user the raw divert block
222 * for him to play with :)
223 */
224 if (copy_to_user(div_cf.arg1.ptr, dev->divert,
225 sizeof(struct divert_blk)))
226 return -EFAULT;
227 break;
228
229 case DIVCMD_GETVERSION:
230 DVDBG("GETVERSION: checking ptr");
231 if (div_cf.arg1.ptr == NULL)
232 return -EINVAL;
233 DVDBG("GETVERSION: copying data to userland");
234 if (copy_to_user(div_cf.arg1.ptr,
235 sysctl_divert_version, 32))
236 return -EFAULT;
237 DVDBG("GETVERSION: data copied");
238 break;
239
240 default:
241 return -EINVAL;
242 };
243
244 break;
245
246 case SIOCSIFDIVERT:
247 if (copy_from_user(&div_cf, arg, sizeof(struct divert_cf)))
248 return -EFAULT;
249
250 ret = check_args(&div_cf, &dev);
251 if (ret)
252 return ret;
253
254 div_blk = dev->divert;
255
256 switch(div_cf.cmd) {
257 case DIVCMD_RESET:
258 div_blk->divert = 0;
259 div_blk->protos = DIVERT_PROTO_NONE;
260 memset(div_blk->tcp_dst, 0,
261 MAX_DIVERT_PORTS * sizeof(u16));
262 memset(div_blk->tcp_src, 0,
263 MAX_DIVERT_PORTS * sizeof(u16));
264 memset(div_blk->udp_dst, 0,
265 MAX_DIVERT_PORTS * sizeof(u16));
266 memset(div_blk->udp_src, 0,
267 MAX_DIVERT_PORTS * sizeof(u16));
268 return 0;
269
270 case DIVCMD_DIVERT:
271 switch(div_cf.arg1.int32) {
272 case DIVARG1_ENABLE:
273 if (div_blk->divert)
274 return -EALREADY;
275 div_blk->divert = 1;
276 break;
277
278 case DIVARG1_DISABLE:
279 if (!div_blk->divert)
280 return -EALREADY;
281 div_blk->divert = 0;
282 break;
283
284 default:
285 return -EINVAL;
286 };
287
288 break;
289
290 case DIVCMD_IP:
291 switch(div_cf.arg1.int32) {
292 case DIVARG1_ENABLE:
293 if (div_blk->protos & DIVERT_PROTO_IP)
294 return -EALREADY;
295 div_blk->protos |= DIVERT_PROTO_IP;
296 break;
297
298 case DIVARG1_DISABLE:
299 if (!(div_blk->protos & DIVERT_PROTO_IP))
300 return -EALREADY;
301 div_blk->protos &= ~DIVERT_PROTO_IP;
302 break;
303
304 default:
305 return -EINVAL;
306 };
307
308 break;
309
310 case DIVCMD_TCP:
311 switch(div_cf.arg1.int32) {
312 case DIVARG1_ENABLE:
313 if (div_blk->protos & DIVERT_PROTO_TCP)
314 return -EALREADY;
315 div_blk->protos |= DIVERT_PROTO_TCP;
316 break;
317
318 case DIVARG1_DISABLE:
319 if (!(div_blk->protos & DIVERT_PROTO_TCP))
320 return -EALREADY;
321 div_blk->protos &= ~DIVERT_PROTO_TCP;
322 break;
323
324 default:
325 return -EINVAL;
326 };
327
328 break;
329
330 case DIVCMD_TCPDST:
331 switch(div_cf.arg1.int32) {
332 case DIVARG1_ADD:
333 return add_port(div_blk->tcp_dst,
334 div_cf.arg2.uint16);
335
336 case DIVARG1_REMOVE:
337 return remove_port(div_blk->tcp_dst,
338 div_cf.arg2.uint16);
339
340 default:
341 return -EINVAL;
342 };
343
344 break;
345
346 case DIVCMD_TCPSRC:
347 switch(div_cf.arg1.int32) {
348 case DIVARG1_ADD:
349 return add_port(div_blk->tcp_src,
350 div_cf.arg2.uint16);
351
352 case DIVARG1_REMOVE:
353 return remove_port(div_blk->tcp_src,
354 div_cf.arg2.uint16);
355
356 default:
357 return -EINVAL;
358 };
359
360 break;
361
362 case DIVCMD_UDP:
363 switch(div_cf.arg1.int32) {
364 case DIVARG1_ENABLE:
365 if (div_blk->protos & DIVERT_PROTO_UDP)
366 return -EALREADY;
367 div_blk->protos |= DIVERT_PROTO_UDP;
368 break;
369
370 case DIVARG1_DISABLE:
371 if (!(div_blk->protos & DIVERT_PROTO_UDP))
372 return -EALREADY;
373 div_blk->protos &= ~DIVERT_PROTO_UDP;
374 break;
375
376 default:
377 return -EINVAL;
378 };
379
380 break;
381
382 case DIVCMD_UDPDST:
383 switch(div_cf.arg1.int32) {
384 case DIVARG1_ADD:
385 return add_port(div_blk->udp_dst,
386 div_cf.arg2.uint16);
387
388 case DIVARG1_REMOVE:
389 return remove_port(div_blk->udp_dst,
390 div_cf.arg2.uint16);
391
392 default:
393 return -EINVAL;
394 };
395
396 break;
397
398 case DIVCMD_UDPSRC:
399 switch(div_cf.arg1.int32) {
400 case DIVARG1_ADD:
401 return add_port(div_blk->udp_src,
402 div_cf.arg2.uint16);
403
404 case DIVARG1_REMOVE:
405 return remove_port(div_blk->udp_src,
406 div_cf.arg2.uint16);
407
408 default:
409 return -EINVAL;
410 };
411
412 break;
413
414 case DIVCMD_ICMP:
415 switch(div_cf.arg1.int32) {
416 case DIVARG1_ENABLE:
417 if (div_blk->protos & DIVERT_PROTO_ICMP)
418 return -EALREADY;
419 div_blk->protos |= DIVERT_PROTO_ICMP;
420 break;
421
422 case DIVARG1_DISABLE:
423 if (!(div_blk->protos & DIVERT_PROTO_ICMP))
424 return -EALREADY;
425 div_blk->protos &= ~DIVERT_PROTO_ICMP;
426 break;
427
428 default:
429 return -EINVAL;
430 };
431
432 break;
433
434 default:
435 return -EINVAL;
436 };
437
438 break;
439
440 default:
441 return -EINVAL;
442 };
443
444 return 0;
445 }
446
447
448 /*
449 * Check if packet should have its dest mac address set to the box itself
450 * for diversion
451 */
452
453 #define ETH_DIVERT_FRAME(skb) \
454 memcpy(skb->mac.ethernet, skb->dev->dev_addr, ETH_ALEN); \
455 skb->pkt_type=PACKET_HOST
456
divert_frame(struct sk_buff * skb)457 void divert_frame(struct sk_buff *skb)
458 {
459 struct ethhdr *eth = skb->mac.ethernet;
460 struct iphdr *iph;
461 struct tcphdr *tcph;
462 struct udphdr *udph;
463 struct divert_blk *divert = skb->dev->divert;
464 int i, src, dst;
465 unsigned char *skb_data_end = skb->data + skb->len;
466
467 /* Packet is already aimed at us, return */
468 if (!memcmp(eth, skb->dev->dev_addr, ETH_ALEN))
469 return;
470
471 /* proto is not IP, do nothing */
472 if (eth->h_proto != htons(ETH_P_IP))
473 return;
474
475 /* Divert all IP frames ? */
476 if (divert->protos & DIVERT_PROTO_IP) {
477 ETH_DIVERT_FRAME(skb);
478 return;
479 }
480
481 /* Check for possible (maliciously) malformed IP frame (thanks Dave) */
482 iph = (struct iphdr *) skb->data;
483 if (((iph->ihl<<2)+(unsigned char*)(iph)) >= skb_data_end) {
484 printk(KERN_INFO "divert: malformed IP packet !\n");
485 return;
486 }
487
488 switch (iph->protocol) {
489 /* Divert all ICMP frames ? */
490 case IPPROTO_ICMP:
491 if (divert->protos & DIVERT_PROTO_ICMP) {
492 ETH_DIVERT_FRAME(skb);
493 return;
494 }
495 break;
496
497 /* Divert all TCP frames ? */
498 case IPPROTO_TCP:
499 if (divert->protos & DIVERT_PROTO_TCP) {
500 ETH_DIVERT_FRAME(skb);
501 return;
502 }
503
504 /* Check for possible (maliciously) malformed IP
505 * frame (thanx Dave)
506 */
507 tcph = (struct tcphdr *)
508 (((unsigned char *)iph) + (iph->ihl<<2));
509 if (((unsigned char *)(tcph+1)) >= skb_data_end) {
510 printk(KERN_INFO "divert: malformed TCP packet !\n");
511 return;
512 }
513
514 /* Divert some tcp dst/src ports only ?*/
515 for (i = 0; i < MAX_DIVERT_PORTS; i++) {
516 dst = divert->tcp_dst[i];
517 src = divert->tcp_src[i];
518 if ((dst && dst == tcph->dest) ||
519 (src && src == tcph->source)) {
520 ETH_DIVERT_FRAME(skb);
521 return;
522 }
523 }
524 break;
525
526 /* Divert all UDP frames ? */
527 case IPPROTO_UDP:
528 if (divert->protos & DIVERT_PROTO_UDP) {
529 ETH_DIVERT_FRAME(skb);
530 return;
531 }
532
533 /* Check for possible (maliciously) malformed IP
534 * packet (thanks Dave)
535 */
536 udph = (struct udphdr *)
537 (((unsigned char *)iph) + (iph->ihl<<2));
538 if (((unsigned char *)(udph+1)) >= skb_data_end) {
539 printk(KERN_INFO
540 "divert: malformed UDP packet !\n");
541 return;
542 }
543
544 /* Divert some udp dst/src ports only ? */
545 for (i = 0; i < MAX_DIVERT_PORTS; i++) {
546 dst = divert->udp_dst[i];
547 src = divert->udp_src[i];
548 if ((dst && dst == udph->dest) ||
549 (src && src == udph->source)) {
550 ETH_DIVERT_FRAME(skb);
551 return;
552 }
553 }
554 break;
555 };
556
557 return;
558 }
559
560