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.
6 * Generic frame diversion
9 * Benoit LOCHER: initial integration within the kernel with support for ethernet
10 * Dave Miller: improvement on the code (correctness, performance and source files)
13 #include <linux/module.h>
14 #include <linux/types.h>
15 #include <linux/kernel.h>
16 #include <linux/sched.h>
17 #include <linux/string.h>
19 #include <linux/socket.h>
21 #include <linux/inet.h>
23 #include <linux/udp.h>
24 #include <linux/netdevice.h>
25 #include <linux/etherdevice.h>
26 #include <linux/skbuff.h>
27 #include <linux/errno.h>
28 #include <linux/init.h>
34 #include <asm/uaccess.h>
35 #include <asm/system.h>
36 #include <asm/checksum.h>
37 #include <linux/divert.h>
38 #include <linux/sockios.h>
40 const char sysctl_divert_version[32]="0.46"; /* Current version */
42 static int __init dv_init(void)
49 * Allocate a divert_blk for a device. This must be an ethernet nic.
51 int alloc_divert_blk(struct net_device *dev)
53 int alloc_size = (sizeof(struct divert_blk) + 3) & ~3;
56 if (dev->type == ARPHRD_ETHER) {
57 dev->divert = (struct divert_blk *)
58 kmalloc(alloc_size, GFP_KERNEL);
59 if (dev->divert == NULL) {
60 printk(KERN_INFO "divert: unable to allocate divert_blk for %s\n",
65 memset(dev->divert, 0, sizeof(struct divert_blk));
73 * Free a divert_blk allocated by the above function, if it was
74 * allocated on that device.
76 void free_divert_blk(struct net_device *dev)
86 * Adds a tcp/udp (source or dest) port to an array
88 static int add_port(u16 ports[], u16 port)
95 /* Storing directly in network format for performance,
100 for (i = 0; i < MAX_DIVERT_PORTS; i++) {
101 if (ports[i] == port)
105 for (i = 0; i < MAX_DIVERT_PORTS; i++) {
116 * Removes a port from an array tcp/udp (source or dest)
118 static int remove_port(u16 ports[], u16 port)
125 /* Storing directly in network format for performance,
130 for (i = 0; i < MAX_DIVERT_PORTS; i++) {
131 if (ports[i] == port) {
140 /* Some basic sanity checks on the arguments passed to divert_ioctl() */
141 static int check_args(struct divert_cf *div_cf, struct net_device **dev)
149 /* GETVERSION: all other args are unused */
150 if (div_cf->cmd == DIVCMD_GETVERSION)
153 /* Network device index should reasonably be between 0 and 1000 :) */
154 if (div_cf->dev_index < 0 || div_cf->dev_index > 1000)
157 /* Let's try to find the ifname */
158 sprintf(devname, "eth%d", div_cf->dev_index);
159 *dev = dev_get_by_name(devname);
161 /* dev should NOT be null */
167 /* user issuing the ioctl must be a super one :) */
168 if (!capable(CAP_SYS_ADMIN)) {
173 /* Device must have a divert_blk member NOT null */
174 if ((*dev)->divert == NULL)
182 * control function of the diverter
186 printk(KERN_DEBUG "divert_ioctl() line %d %s\n", __LINE__, (a))
191 int divert_ioctl(unsigned int cmd, struct divert_cf __user *arg)
193 struct divert_cf div_cf;
194 struct divert_blk *div_blk;
195 struct net_device *dev;
200 DVDBG("SIOCGIFDIVERT, copy_from_user");
201 if (copy_from_user(&div_cf, arg, sizeof(struct divert_cf)))
203 DVDBG("before check_args");
204 ret = check_args(&div_cf, &dev);
207 DVDBG("after checkargs");
208 div_blk = dev->divert;
210 DVDBG("befre switch()");
211 switch (div_cf.cmd) {
212 case DIVCMD_GETSTATUS:
213 /* Now, just give the user the raw divert block
214 * for him to play with :)
216 if (copy_to_user(div_cf.arg1.ptr, dev->divert,
217 sizeof(struct divert_blk)))
221 case DIVCMD_GETVERSION:
222 DVDBG("GETVERSION: checking ptr");
223 if (div_cf.arg1.ptr == NULL)
225 DVDBG("GETVERSION: copying data to userland");
226 if (copy_to_user(div_cf.arg1.ptr,
227 sysctl_divert_version, 32))
229 DVDBG("GETVERSION: data copied");
239 if (copy_from_user(&div_cf, arg, sizeof(struct divert_cf)))
242 ret = check_args(&div_cf, &dev);
246 div_blk = dev->divert;
251 div_blk->protos = DIVERT_PROTO_NONE;
252 memset(div_blk->tcp_dst, 0,
253 MAX_DIVERT_PORTS * sizeof(u16));
254 memset(div_blk->tcp_src, 0,
255 MAX_DIVERT_PORTS * sizeof(u16));
256 memset(div_blk->udp_dst, 0,
257 MAX_DIVERT_PORTS * sizeof(u16));
258 memset(div_blk->udp_src, 0,
259 MAX_DIVERT_PORTS * sizeof(u16));
263 switch(div_cf.arg1.int32) {
270 case DIVARG1_DISABLE:
271 if (!div_blk->divert)
283 switch(div_cf.arg1.int32) {
285 if (div_blk->protos & DIVERT_PROTO_IP)
287 div_blk->protos |= DIVERT_PROTO_IP;
290 case DIVARG1_DISABLE:
291 if (!(div_blk->protos & DIVERT_PROTO_IP))
293 div_blk->protos &= ~DIVERT_PROTO_IP;
303 switch(div_cf.arg1.int32) {
305 if (div_blk->protos & DIVERT_PROTO_TCP)
307 div_blk->protos |= DIVERT_PROTO_TCP;
310 case DIVARG1_DISABLE:
311 if (!(div_blk->protos & DIVERT_PROTO_TCP))
313 div_blk->protos &= ~DIVERT_PROTO_TCP;
323 switch(div_cf.arg1.int32) {
325 return add_port(div_blk->tcp_dst,
329 return remove_port(div_blk->tcp_dst,
339 switch(div_cf.arg1.int32) {
341 return add_port(div_blk->tcp_src,
345 return remove_port(div_blk->tcp_src,
355 switch(div_cf.arg1.int32) {
357 if (div_blk->protos & DIVERT_PROTO_UDP)
359 div_blk->protos |= DIVERT_PROTO_UDP;
362 case DIVARG1_DISABLE:
363 if (!(div_blk->protos & DIVERT_PROTO_UDP))
365 div_blk->protos &= ~DIVERT_PROTO_UDP;
375 switch(div_cf.arg1.int32) {
377 return add_port(div_blk->udp_dst,
381 return remove_port(div_blk->udp_dst,
391 switch(div_cf.arg1.int32) {
393 return add_port(div_blk->udp_src,
397 return remove_port(div_blk->udp_src,
407 switch(div_cf.arg1.int32) {
409 if (div_blk->protos & DIVERT_PROTO_ICMP)
411 div_blk->protos |= DIVERT_PROTO_ICMP;
414 case DIVARG1_DISABLE:
415 if (!(div_blk->protos & DIVERT_PROTO_ICMP))
417 div_blk->protos &= ~DIVERT_PROTO_ICMP;
441 * Check if packet should have its dest mac address set to the box itself
445 #define ETH_DIVERT_FRAME(skb) \
446 memcpy(eth_hdr(skb), skb->dev->dev_addr, ETH_ALEN); \
447 skb->pkt_type=PACKET_HOST
449 void divert_frame(struct sk_buff *skb)
451 struct ethhdr *eth = eth_hdr(skb);
455 struct divert_blk *divert = skb->dev->divert;
457 unsigned char *skb_data_end = skb->data + skb->len;
459 /* Packet is already aimed at us, return */
460 if (!memcmp(eth, skb->dev->dev_addr, ETH_ALEN))
463 /* proto is not IP, do nothing */
464 if (eth->h_proto != htons(ETH_P_IP))
467 /* Divert all IP frames ? */
468 if (divert->protos & DIVERT_PROTO_IP) {
469 ETH_DIVERT_FRAME(skb);
473 /* Check for possible (maliciously) malformed IP frame (thanks Dave) */
474 iph = (struct iphdr *) skb->data;
475 if (((iph->ihl<<2)+(unsigned char*)(iph)) >= skb_data_end) {
476 printk(KERN_INFO "divert: malformed IP packet !\n");
480 switch (iph->protocol) {
481 /* Divert all ICMP frames ? */
483 if (divert->protos & DIVERT_PROTO_ICMP) {
484 ETH_DIVERT_FRAME(skb);
489 /* Divert all TCP frames ? */
491 if (divert->protos & DIVERT_PROTO_TCP) {
492 ETH_DIVERT_FRAME(skb);
496 /* Check for possible (maliciously) malformed IP
499 tcph = (struct tcphdr *)
500 (((unsigned char *)iph) + (iph->ihl<<2));
501 if (((unsigned char *)(tcph+1)) >= skb_data_end) {
502 printk(KERN_INFO "divert: malformed TCP packet !\n");
506 /* Divert some tcp dst/src ports only ?*/
507 for (i = 0; i < MAX_DIVERT_PORTS; i++) {
508 dst = divert->tcp_dst[i];
509 src = divert->tcp_src[i];
510 if ((dst && dst == tcph->dest) ||
511 (src && src == tcph->source)) {
512 ETH_DIVERT_FRAME(skb);
518 /* Divert all UDP frames ? */
520 if (divert->protos & DIVERT_PROTO_UDP) {
521 ETH_DIVERT_FRAME(skb);
525 /* Check for possible (maliciously) malformed IP
526 * packet (thanks Dave)
528 udph = (struct udphdr *)
529 (((unsigned char *)iph) + (iph->ihl<<2));
530 if (((unsigned char *)(udph+1)) >= skb_data_end) {
532 "divert: malformed UDP packet !\n");
536 /* Divert some udp dst/src ports only ? */
537 for (i = 0; i < MAX_DIVERT_PORTS; i++) {
538 dst = divert->udp_dst[i];
539 src = divert->udp_src[i];
540 if ((dst && dst == udph->dest) ||
541 (src && src == udph->source)) {
542 ETH_DIVERT_FRAME(skb);