int udp_connect(struct sock *sk, struct sockaddr_in *usin, int addr_len) { struct rtable *rt; if (addr_len < sizeof(*usin)) return(-EINVAL); if (usin->sin_family && usin->sin_family != AF_INET) return(-EAFNOSUPPORT); if (usin->sin_addr.s_addr==INADDR_ANY) usin->sin_addr.s_addr=ip_my_addr(); if(!sk->broadcast && ip_chk_addr(usin->sin_addr.s_addr)==IS_BROADCAST) return -EACCES; /* Must turn broadcast on first */ rt=ip_rt_route((__u32)usin->sin_addr.s_addr, sk->localroute, sk->bound_device); if (rt==NULL) return -ENETUNREACH; if(!sk->saddr) sk->saddr = rt->rt_src; /* Update source address */ if(!sk->rcv_saddr) sk->rcv_saddr = rt->rt_src; sk->daddr = usin->sin_addr.s_addr; sk->dummy_th.dest = usin->sin_port; sk->state = TCP_ESTABLISHED; if (sk->ip_route_cache) ip_rt_put(sk->ip_route_cache); sk->ip_route_cache = rt; return(0); }
int icmp_chkaddr(struct sk_buff *skb) { struct icmphdr *icmph=(struct icmphdr *)(skb->h.raw + skb->h.iph->ihl*4); struct iphdr *iph = (struct iphdr *) (icmph + 1); void (*handler)(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev, __u32 saddr, __u32 daddr, int len) = icmp_pointers[icmph->type].handler; if (handler == icmp_unreach || handler == icmp_redirect) { struct sock *sk; switch (iph->protocol) { case IPPROTO_TCP: { struct tcphdr *th = (struct tcphdr *)(((unsigned char *)iph)+(iph->ihl<<2)); sk = tcp_v4_lookup(iph->saddr, th->source, iph->daddr, th->dest, skb->dev); if (!sk) return 0; if (sk->saddr != iph->saddr) return 0; if (sk->daddr != iph->daddr) return 0; if (sk->dummy_th.dest != th->dest) return 0; /* * This packet came from us. */ return 1; } case IPPROTO_UDP: { struct udphdr *uh = (struct udphdr *)(((unsigned char *)iph)+(iph->ihl<<2)); sk = udp_v4_lookup(iph->saddr, uh->source, iph->daddr, uh->dest, skb->dev); if (!sk) return 0; if (sk->saddr != iph->saddr && ip_chk_addr(iph->saddr) != IS_MYADDR) return 0; /* * This packet may have come from us. * Assume it did. */ return 1; } } } return 0; }
int arp_find(unsigned char *haddr, unsigned long paddr, struct device *dev, unsigned long saddr, struct sk_buff *skb) { struct arp_table *entry; unsigned long hash; #ifdef CONFIG_IP_MULTICAST unsigned long taddr; #endif switch (ip_chk_addr(paddr)) { case IS_MYADDR: printk("ARP: arp called for own IP address\n"); memcpy(haddr, dev->dev_addr, dev->addr_len); skb->arp = 1; return 0; #ifdef CONFIG_IP_MULTICAST case IS_MULTICAST: if(dev->type==ARPHRD_ETHER || dev->type==ARPHRD_IEEE802) { haddr[0]=0x01; haddr[1]=0x00; haddr[2]=0x5e; taddr=ntohl(paddr); haddr[5]=taddr&0xff; taddr=taddr>>8; haddr[4]=taddr&0xff; taddr=taddr>>8; haddr[3]=taddr&0x7f; return 0; } /* * If a device does not support multicast broadcast the stuff (eg AX.25 for now) */ #endif case IS_BROADCAST: memcpy(haddr, dev->broadcast, dev->addr_len); skb->arp = 1; return 0; }
void ip_rt_redirect(__u32 src, __u32 dst, __u32 gw, struct device *dev) { struct rt_req * rtr; struct rtable * rt; rt = ip_rt_route(dst, 0, NULL); if (!rt) return; if (rt->rt_gateway != src || rt->rt_dev != dev || ((gw^dev->pa_addr)&dev->pa_mask) || ip_chk_addr(gw)) { ip_rt_put(rt); return; } ip_rt_put(rt); ip_rt_fast_lock(); if (ip_rt_lock == 1) { rt_redirect_1(dst, gw, dev); ip_rt_unlock(); return; } rtr = kmalloc(sizeof(struct rt_req), GFP_ATOMIC); if (rtr) { rtr->dst = dst; rtr->gw = gw; rtr->dev = dev; rt_req_enqueue(&rt_backlog, rtr); ip_rt_bh_mask |= RT_BH_REDIRECT; } ip_rt_unlock(); }
int ip_options_compile(unsigned char *iph) { int l; unsigned char *optptr; int optlen; unsigned char *pp_ptr = 0; char optholder[16]; struct options *opt; int skb = 1; int skb_pa_addr = 314159; opt = (struct options *) optholder; memset(opt, 0, sizeof(struct options)); opt->optlen = ((struct iphdr *) iph)->ihl * 4 - sizeof(struct iphdr); optptr = iph + sizeof(struct iphdr); opt->is_data = 0; for (l = opt->optlen; l > 0;) { switch (*optptr) { case IPOPT_END: for (optptr++, l--; l > 0; l--) { if (*optptr != IPOPT_END) { *optptr = IPOPT_END; opt->is_changed = 1; } } goto eol; case IPOPT_NOOP: l--; optptr++; continue; } optlen = optptr[1]; if (optlen < 2 || optlen > l) { pp_ptr = optptr; goto error; } switch (*optptr) { case IPOPT_SSRR: case IPOPT_LSRR: if (optlen < 3) { pp_ptr = optptr + 1; goto error; } if (optptr[2] < 4) { pp_ptr = optptr + 2; goto error; } /* NB: cf RFC-1812 5.2.4.1 */ if (opt->srr) { pp_ptr = optptr; goto error; } if (!skb) { if (optptr[2] != 4 || optlen < 7 || ((optlen - 3) & 3)) { pp_ptr = optptr + 1; goto error; } memcpy(&opt->faddr, &optptr[3], 4); if (optlen > 7) memmove(&optptr[3], &optptr[7], optlen - 7); } opt->is_strictroute = (optptr[0] == IPOPT_SSRR); opt->srr = optptr - iph; break; case IPOPT_RR: if (opt->rr) { pp_ptr = optptr; goto error; } if (optlen < 3) { pp_ptr = optptr + 1; goto error; } if (optptr[2] < 4) { pp_ptr = optptr + 2; goto error; } if (optptr[2] <= optlen) { if (optptr[2] + 3 > optlen) { pp_ptr = optptr + 2; goto error; } if (skb) { memcpy(&optptr[optptr[2] - 1], &skb_pa_addr, 4); opt->is_changed = 1; } optptr[2] += 4; opt->rr_needaddr = 1; } opt->rr = optptr - iph; break; case IPOPT_TIMESTAMP: if (opt->ts) { pp_ptr = optptr; goto error; } if (optlen < 4) { pp_ptr = optptr + 1; goto error; } if (optptr[2] < 5) { pp_ptr = optptr + 2; goto error; } if (optptr[2] <= optlen) { struct timestamp *ts = (struct timestamp *) (optptr + 1); __u32 *timeptr = 0; if (ts->ptr + 3 > ts->len) { pp_ptr = optptr + 2; goto error; } switch (ts->flags) { case IPOPT_TS_TSONLY: opt->ts = optptr - iph; if (skb) timeptr = (__u32 *) & optptr[ts->ptr - 1]; opt->ts_needtime = 1; ts->ptr += 4; break; case IPOPT_TS_TSANDADDR: if (ts->ptr + 7 > ts->len) { pp_ptr = optptr + 2; goto error; } opt->ts = optptr - iph; if (skb) { memcpy(&optptr[ts->ptr - 1], &skb_pa_addr, 4); timeptr = (__u32 *) & optptr[ts->ptr + 3]; } opt->ts_needaddr = 1; opt->ts_needtime = 1; ts->ptr += 8; break; case IPOPT_TS_PRESPEC: if (ts->ptr + 7 > ts->len) { pp_ptr = optptr + 2; goto error; } opt->ts = optptr - iph; { __u32 addr; memcpy(&addr, &optptr[ts->ptr - 1], 4); if (ip_chk_addr(addr) == 0) break; if (skb) timeptr = (__u32 *) & optptr[ts->ptr + 3]; } opt->ts_needaddr = 1; opt->ts_needtime = 1; ts->ptr += 8; break; default: pp_ptr = optptr + 3; goto error; } if (timeptr) { //struct timeval tv; __u32 midtime = 1; //do_gettimeofday(&tv); //midtime = htonl((tv.tv_sec % 86400) * 1000 + tv.tv_usec / 1000); memcpy(timeptr, &midtime, sizeof(__u32)); opt->is_changed = 1; } } else { struct timestamp *ts = (struct timestamp *) (optptr + 1); if (ts->overflow == 15) { pp_ptr = optptr + 3; goto error; } opt->ts = optptr - iph; if (skb) { ts->overflow++; opt->is_changed = 1; } } break; case IPOPT_SEC: case IPOPT_SID: default: if (!skb) { pp_ptr = optptr; goto error; } break; } l -= optlen; optptr += optlen; } eol: if (!pp_ptr) if (!((struct options *) optholder)->srr) return 0; error: return -1; }
static int inet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) { struct sockaddr_in *addr=(struct sockaddr_in *)uaddr; struct sock *sk=(struct sock *)sock->data, *sk2; unsigned short snum = 0 /* Stoopid compiler.. this IS ok */; int chk_addr_ret; /* check this error. */ if (sk->state != TCP_CLOSE) return(-EIO); if(addr_len<sizeof(struct sockaddr_in)) return -EINVAL; if(sock->type != SOCK_RAW) { /* 验证sk已经加入到对应传输层协议的管理结构中 */ if (sk->num != 0) return(-EINVAL); snum = ntohs(addr->sin_port); /* * We can't just leave the socket bound wherever it is, it might * be bound to a privileged port. However, since there seems to * be a bug here, we will leave it if the port is not privileged. */ if (snum == 0) { snum = get_new_socknum(sk->prot, 0); } if (snum < PROT_SOCK && !suser()) return(-EACCES); } chk_addr_ret = ip_chk_addr(addr->sin_addr.s_addr); if (addr->sin_addr.s_addr != 0 && chk_addr_ret != IS_MYADDR && chk_addr_ret != IS_MULTICAST) return(-EADDRNOTAVAIL); /* Source address MUST be ours! */ if (chk_addr_ret || addr->sin_addr.s_addr == 0) sk->saddr = addr->sin_addr.s_addr; if(sock->type != SOCK_RAW) { /* Make sure we are allowed to bind here. */ cli(); for(sk2 = sk->prot->sock_array[snum & (SOCK_ARRAY_SIZE -1)]; sk2 != NULL; sk2 = sk2->next) { /* should be below! */ if (sk2->num != snum) continue; if (!sk->reuse) { sti(); return(-EADDRINUSE); } if (sk2->num != snum) continue; /* more than one */ if (sk2->saddr != sk->saddr) continue; /* socket per slot ! -FB */ if (!sk2->reuse || sk2->state==TCP_LISTEN) { sti(); return(-EADDRINUSE); } } sti(); remove_sock(sk); put_sock(snum, sk); sk->dummy_th.source = ntohs(sk->num); sk->daddr = 0; sk->dummy_th.dest = 0; } return(0); }
static int raw_sendto(struct sock *sk, const unsigned char *from, int len, int noblock, unsigned flags, struct sockaddr_in *usin, int addr_len) { int err; struct sockaddr_in sin; /* * Check the flags. Only MSG_DONTROUTE is permitted. */ if (flags & MSG_OOB) /* Mirror BSD error message compatibility */ return -EOPNOTSUPP; if (flags & ~MSG_DONTROUTE) return(-EINVAL); /* * Get and verify the address. */ if (usin) { if (addr_len < sizeof(sin)) return(-EINVAL); memcpy(&sin, usin, sizeof(sin)); if (sin.sin_family && sin.sin_family != AF_INET) return(-EINVAL); } else { if (sk->state != TCP_ESTABLISHED) return(-EINVAL); sin.sin_family = AF_INET; sin.sin_port = sk->num; sin.sin_addr.s_addr = sk->daddr; } if (sin.sin_port == 0) sin.sin_port = sk->num; if (sin.sin_addr.s_addr == INADDR_ANY) sin.sin_addr.s_addr = ip_my_addr(); /* * BSD raw sockets forget to check SO_BROADCAST .... */ if (!sk->bsdism && sk->broadcast == 0 && ip_chk_addr(sin.sin_addr.s_addr)==IS_BROADCAST) return -EACCES; if(sk->ip_hdrincl) { if(len>65535) return -EMSGSIZE; err=ip_build_xmit(sk, raw_getrawfrag, from, len, sin.sin_addr.s_addr, 0, sk->opt, flags, sin.sin_port, noblock); } else { if(len>65535-sizeof(struct iphdr)) return -EMSGSIZE; err=ip_build_xmit(sk, raw_getfrag, from, len, sin.sin_addr.s_addr, 0, sk->opt, flags, sin.sin_port, noblock); } return err<0?err:len; }
int udp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt, __u32 daddr, unsigned short len, __u32 saddr, int redo, struct inet_protocol *protocol) { struct sock *sk; struct udphdr *uh; unsigned short ulen; int addr_type; /* * If we're doing a "redo" (the socket was busy last time * around), we can just queue the packet now.. */ if (redo) { udp_queue_rcv_skb(skb->sk, skb); return 0; } /* * First time through the loop.. Do all the setup stuff * (including finding out the socket we go to etc) */ addr_type = IS_MYADDR; if(!dev || dev->pa_addr!=daddr) addr_type=ip_chk_addr(daddr); /* * Get the header. */ uh = (struct udphdr *) skb->h.uh; ip_statistics.IpInDelivers++; /* * Validate the packet and the UDP length. */ ulen = ntohs(uh->len); if (ulen > len || len < sizeof(*uh) || ulen < sizeof(*uh)) { NETDEBUG(printk("UDP: short packet: %d/%d\n", ulen, len)); udp_statistics.UdpInErrors++; kfree_skb(skb, FREE_WRITE); return(0); } /* RFC1122 warning: According to 4.1.3.6, we MUST discard any */ /* datagram which has an invalid source address, either here or */ /* in IP. */ /* Right now, IP isn't doing it, and neither is UDP. It's on the */ /* FIXME list for IP, though, so I wouldn't worry about it. */ /* (That's the Right Place to do it, IMHO.) -- MS */ if (uh->check && ( ( (skb->ip_summed == CHECKSUM_HW) && udp_check(uh, len, saddr, daddr, skb->csum ) ) || ( (skb->ip_summed == CHECKSUM_NONE) && udp_check(uh, len, saddr, daddr,csum_partial((char*)uh, len, 0))) /* skip if CHECKSUM_UNNECESSARY */ ) ) { /* <*****@*****.**> wants to know, who sent it, to go and stomp on the garbage sender... */ /* RFC1122: OK. Discards the bad packet silently (as far as */ /* the network is concerned, anyway) as per 4.1.3.4 (MUST). */ NETDEBUG(printk("UDP: bad checksum. From %08lX:%d to %08lX:%d ulen %d\n", ntohl(saddr),ntohs(uh->source), ntohl(daddr),ntohs(uh->dest), ulen)); udp_statistics.UdpInErrors++; kfree_skb(skb, FREE_WRITE); return(0); } /* * These are supposed to be switched. */ skb->daddr = saddr; skb->saddr = daddr; len=ulen; skb->dev = dev; skb_trim(skb,len); #ifdef CONFIG_IP_MULTICAST if (addr_type==IS_BROADCAST || addr_type==IS_MULTICAST) return udp_v4_mcast_deliver(skb, uh, saddr, daddr); #endif #ifdef CONFIG_IP_TRANSPARENT_PROXY if(skb->redirport) sk = udp_v4_proxy_lookup(saddr, uh->source, daddr, uh->dest, dev->pa_addr, skb->redirport, dev); else #endif sk = udp_v4_lookup(saddr, uh->source, daddr, uh->dest, dev); if (sk == NULL) { udp_statistics.UdpNoPorts++; if (addr_type != IS_BROADCAST && addr_type != IS_MULTICAST) { icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0, dev); } /* * Hmm. We got an UDP broadcast to a port to which we * don't wanna listen. Ignore it. */ skb->sk = NULL; kfree_skb(skb, FREE_WRITE); return(0); } udp_deliver(sk, skb); return 0; }
static int udp_sendto(struct sock *sk, const unsigned char *from, int len, int noblock, unsigned flags, struct sockaddr_in *usin, int addr_len) { struct sockaddr_in sin; int tmp; __u32 saddr=0; /* * Check the flags. We support no flags for UDP sending */ #ifdef CONFIG_IP_TRANSPARENT_PROXY if (flags&~(MSG_DONTROUTE|MSG_PROXY)) #else if (flags&~MSG_DONTROUTE) #endif return(-EINVAL); /* * Get and verify the address. */ if (usin) { if (addr_len < sizeof(sin)) return(-EINVAL); if (usin->sin_family && usin->sin_family != AF_INET) return(-EINVAL); if (usin->sin_port == 0) return(-EINVAL); } else { #ifdef CONFIG_IP_TRANSPARENT_PROXY /* We need to provide a sockaddr_in when using MSG_PROXY. */ if (flags&MSG_PROXY) return(-EINVAL); #endif if (sk->state != TCP_ESTABLISHED) return(-EINVAL); sin.sin_family = AF_INET; sin.sin_port = sk->dummy_th.dest; sin.sin_addr.s_addr = sk->daddr; usin = &sin; } /* * BSD socket semantics. You must set SO_BROADCAST to permit * broadcasting of data. */ /* RFC1122: OK. Allows the application to select the specific */ /* source address for an outgoing packet (MUST) as per 4.1.3.5. */ /* Optional addition: a mechanism for telling the application what */ /* address was used. (4.1.3.5, MAY) -- MS */ /* RFC1122: MUST ensure that all outgoing packets have one */ /* of this host's addresses as a source addr.(4.1.3.6) - bind in */ /* af_inet.c checks these. It does need work to allow BSD style */ /* bind to multicast as is done by xntpd */ if(usin->sin_addr.s_addr==INADDR_ANY) usin->sin_addr.s_addr=ip_my_addr(); if(!sk->broadcast && ip_chk_addr(usin->sin_addr.s_addr)==IS_BROADCAST) return -EACCES; /* Must turn broadcast on first */ lock_sock(sk); /* Send the packet. */ tmp = udp_send(sk, usin, from, len, flags, saddr, noblock); /* The datagram has been sent off. Release the socket. */ release_sock(sk); return(tmp); }
void icmp_send(struct sk_buff *skb_in, int type, int code, unsigned long info, struct device *dev) { struct iphdr *iph; struct icmphdr *icmph; int atype, room; struct icmp_bxm icmp_param; __u32 saddr; /* * Find the original header */ iph = skb_in->ip_hdr; /* * No replies to physical multicast/broadcast */ if(skb_in->pkt_type!=PACKET_HOST) return; /* * Now check at the protocol level */ atype=ip_chk_addr(iph->daddr); if(atype==IS_BROADCAST||atype==IS_MULTICAST) return; /* * Only reply to fragment 0. We byte re-order the constant * mask for efficiency. */ if(iph->frag_off&htons(IP_OFFSET)) return; /* * If we send an ICMP error to an ICMP error a mess would result.. */ if(icmp_pointers[type].error) { /* * We are an error, check if we are replying to an ICMP error */ if(iph->protocol==IPPROTO_ICMP) { icmph = (struct icmphdr *)((char *)iph + (iph->ihl<<2)); /* * Assume any unknown ICMP type is an error. This isn't * specified by the RFC, but think about it.. */ if(icmph->type>18 || icmp_pointers[icmph->type].error) return; } } /* * Check the rate limit */ #ifndef CONFIG_NO_ICMP_LIMIT if (!xrlim_allow(type, iph->saddr)) return; #endif /* * Construct source address and options. */ saddr=iph->daddr; if(saddr!=dev->pa_addr && dev->pa_addr != 0 && ip_chk_addr(saddr)!=IS_MYADDR) saddr=dev->pa_addr; if(ip_options_echo(&icmp_param.replyopts, NULL, saddr, iph->saddr, skb_in)) return; /* * Prepare data for ICMP header. */ icmp_param.icmph.type=type; icmp_param.icmph.code=code; icmp_param.icmph.un.gateway = info; icmp_param.data_ptr=iph; room = 576 - sizeof(struct iphdr) - icmp_param.replyopts.optlen; icmp_param.data_len=(iph->ihl<<2)+skb_in->len; /* RFC says return as much as we can without exceeding 576 bytes */ if (icmp_param.data_len > room) icmp_param.data_len = room; /* * Build and send the packet. */ icmp_build_xmit(&icmp_param, saddr, iph->saddr, icmp_pointers[type].error ? (iph->tos & 0x1E) | 0xC0 : iph->tos); }
int icmp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt, __u32 daddr, unsigned short len, __u32 saddr, int redo, struct inet_protocol *protocol) { struct icmphdr *icmph=(void *)skb->h.raw; #ifdef CONFIG_IP_TRANSPARENT_PROXY int r; #endif icmp_statistics.IcmpInMsgs++; if(len < sizeof(struct icmphdr)) { icmp_statistics.IcmpInErrors++; NETDEBUG(printk(KERN_INFO "ICMP: runt packet\n")); kfree_skb(skb, FREE_READ); return 0; } /* * Validate the packet */ if (ip_compute_csum((unsigned char *) icmph, len)) { /* Failed checksum! */ icmp_statistics.IcmpInErrors++; NETDEBUG(printk(KERN_INFO "ICMP: failed checksum from %s!\n", in_ntoa(saddr))); kfree_skb(skb, FREE_READ); return(0); } /* * 18 is the highest 'known' ICMP type. Anything else is a mystery * * RFC 1122: 3.2.2 Unknown ICMP messages types MUST be silently discarded. */ if(icmph->type > 18) { icmp_statistics.IcmpInErrors++; /* Is this right - or do we ignore ? */ kfree_skb(skb,FREE_READ); return(0); } /* * Parse the ICMP message */ #ifdef CONFIG_IP_TRANSPARENT_PROXY /* * We may get non-local addresses and still want to handle them * locally, due to transparent proxying. * Thus, narrow down the test to what is really meant. */ if (daddr!=dev->pa_addr && ((r = ip_chk_addr(daddr)) == IS_BROADCAST || r == IS_MULTICAST)) #else if (daddr && daddr!=dev->pa_addr && ip_chk_addr(daddr) != IS_MYADDR) #endif { /* * RFC 1122: 3.2.2.6 An ICMP_ECHO to broadcast MAY be silently ignored (we don't as it is used * by some network mapping tools). * RFC 1122: 3.2.2.8 An ICMP_TIMESTAMP MAY be silently discarded if to broadcast/multicast. */ if (icmph->type != ICMP_ECHO) { icmp_statistics.IcmpInErrors++; kfree_skb(skb, FREE_READ); return(0); } /* * Reply the multicast/broadcast using a legal * interface - in this case the device we got * it from. */ daddr=dev->pa_addr; } len-=sizeof(struct icmphdr); (*icmp_pointers[icmph->type].input)++; (icmp_pointers[icmph->type].handler)(icmph,skb,skb->dev,saddr,daddr,len); return 0; }
/* * IPSEC <> netlink interface * Copyright (C) 1996, 1997 John Ioannidis. * Copyright (C) 1998, 1999, 2000, 2001 Richard Guy Briggs. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. */ char ipsec_netlink_c_version[] = "RCSID $Id: ipsec_netlink.c,v 1.56 2002/01/29 17:17:55 mcr Exp $"; #include <linux/config.h> #include <linux/version.h> #include <linux/kernel.h> /* printk() */ #include "ipsec_param.h" #ifdef MALLOC_SLAB # include <linux/slab.h> /* kmalloc() */ #else /* MALLOC_SLAB */ # include <linux/malloc.h> /* kmalloc() */ #endif /* MALLOC_SLAB */ #include <linux/errno.h> /* error codes */ #include <linux/types.h> /* size_t */ #include <linux/interrupt.h> /* mark_bh */ #include <linux/netdevice.h> /* struct device, and other headers */ #include <linux/etherdevice.h> /* eth_type_trans */ #include <linux/ip.h> /* struct iphdr */ #include <linux/skbuff.h> #include <freeswan.h> #ifdef SPINLOCK # ifdef SPINLOCK_23 # include <linux/spinlock.h> /* *lock* */ # else /* 23_SPINLOCK */ # include <asm/spinlock.h> /* *lock* */ # endif /* 23_SPINLOCK */ #endif /* SPINLOCK */ #ifdef NET_21 # include <asm/uaccess.h> # include <linux/in6.h> # define ip_chk_addr inet_addr_type # define IS_MYADDR RTN_LOCAL #endif #include <asm/checksum.h> #include <net/ip.h> #ifdef NETLINK_SOCK # include <linux/netlink.h> #else # include <net/netlink.h> #endif #include "radij.h" #include "ipsec_encap.h" #include "ipsec_radij.h" #include "ipsec_netlink.h" #include "ipsec_xform.h" #include "ipsec_rcv.h" #include "ipsec_ah.h" #include "ipsec_esp.h" #ifdef CONFIG_IPSEC_DEBUG # include "ipsec_tunnel.h" #endif /* CONFIG_IPSEC_DEBUG */ #include <pfkeyv2.h> #include <pfkey.h> #ifdef CONFIG_IPSEC_DEBUG int debug_netlink = 0; #endif /* CONFIG_IPSEC_DEBUG */ #define SENDERR(_x) do { len = -(_x); goto errlab; } while (0) #if 0 int #ifdef NETLINK_SOCK ipsec_callback(int proto, struct sk_buff *skb) #else /* NETLINK_SOCK */ ipsec_callback(struct sk_buff *skb) #endif /* NETLINK_SOCK */ { /* * this happens when we write to /dev/ipsec (c 36 10) */ int len = skb->len; u_char *dat = (u_char *)skb->data; struct encap_msghdr *em = (struct encap_msghdr *)dat; struct tdb *tdbp, *tprev; int i, nspis, error = 0; #ifdef CONFIG_IPSEC_DEBUG struct eroute *eret; char sa[SATOA_BUF]; size_t sa_len; struct sk_buff *first, *last; sa_len = satoa(em->em_said, 0, sa, SATOA_BUF); if(debug_netlink) { printk("klips_debug:ipsec_callback: " "skb=0x%p skblen=%ld em_magic=%d em_type=%d\n", skb, (unsigned long int)skb->len, em->em_magic, em->em_type); switch(em->em_type) { case EMT_SETDEBUG: printk("klips_debug:ipsec_callback: " "set ipsec_debug level\n"); break; case EMT_DELEROUTE: case EMT_CLREROUTE: case EMT_CLRSPIS: break; default: printk("klips_debug:ipsec_callback: " "called for SA:%s\n", sa_len ? sa : " (error)"); } } #endif /* CONFIG_IPSEC_DEBUG */ /* XXXX Temporarily disable netlink I/F code until it gets permanantly ripped out in favour of PF_KEYv2 I/F. */ SENDERR(EPROTONOSUPPORT); /* em = (struct encap_msghdr *)dat; */ if (em->em_magic != EM_MAGIC) { printk("klips_debug:ipsec_callback: " "bad magic=%d failed, should be %d\n", em->em_magic, EM_MAGIC); SENDERR(EINVAL); } switch (em->em_type) { case EMT_SETDEBUG: #ifdef CONFIG_IPSEC_DEBUG if(em->em_db_nl >> (sizeof(em->em_db_nl) * 8 - 1)) { em->em_db_nl &= ~(1 << (sizeof(em->em_db_nl) * 8 -1)); debug_tunnel |= em->em_db_tn; debug_netlink |= em->em_db_nl; debug_xform |= em->em_db_xf; debug_eroute |= em->em_db_er; debug_spi |= em->em_db_sp; debug_radij |= em->em_db_rj; debug_esp |= em->em_db_es; debug_ah |= em->em_db_ah; debug_rcv |= em->em_db_rx; debug_pfkey |= em->em_db_ky; if(debug_netlink) printk("klips_debug:ipsec_callback: set\n"); } else { if(debug_netlink) printk("klips_debug:ipsec_callback: unset\n"); debug_tunnel &= em->em_db_tn; debug_netlink &= em->em_db_nl; debug_xform &= em->em_db_xf; debug_eroute &= em->em_db_er; debug_spi &= em->em_db_sp; debug_radij &= em->em_db_rj; debug_esp &= em->em_db_es; debug_ah &= em->em_db_ah; debug_rcv &= em->em_db_rx; debug_pfkey &= em->em_db_ky; } #else /* CONFIG_IPSEC_DEBUG */ printk("klips_debug:ipsec_callback: " "debugging not enabled\n"); SENDERR(EINVAL); #endif /* CONFIG_IPSEC_DEBUG */ break; case EMT_SETEROUTE: if ((error = ipsec_makeroute(&(em->em_eaddr), &(em->em_emask), em->em_ersaid, 0, NULL, NULL, NULL))) SENDERR(-error); break; case EMT_REPLACEROUTE: if ((error = ipsec_breakroute(&(em->em_eaddr), &(em->em_emask), &first, &last)) == EINVAL) { kfree_skb(first); kfree_skb(last); SENDERR(-error); } if ((error = ipsec_makeroute(&(em->em_eaddr), &(em->em_emask), em->em_ersaid, NULL, NULL))) SENDERR(-error); break; case EMT_DELEROUTE: if ((error = ipsec_breakroute(&(em->em_eaddr), &(em->em_emask), &first, &last))) kfree_skb(first); kfree_skb(last); SENDERR(-error); break; case EMT_CLREROUTE: if ((error = ipsec_cleareroutes())) SENDERR(-error); break; case EMT_SETSPI: if (em->em_if >= 5) /* XXX -- why 5? */ SENDERR(ENODEV); tdbp = gettdb(&(em->em_said)); if (tdbp == NULL) { tdbp = (struct tdb *)kmalloc(sizeof (*tdbp), GFP_ATOMIC); if (tdbp == NULL) SENDERR(ENOBUFS); memset((caddr_t)tdbp, 0, sizeof(*tdbp)); tdbp->tdb_said = em->em_said; tdbp->tdb_flags = em->em_flags; if(ip_chk_addr((unsigned long)em->em_said.dst.s_addr) == IS_MYADDR) { tdbp->tdb_flags |= EMT_INBOUND; } KLIPS_PRINT(debug_netlink & DB_NL_TDBCB, "klips_debug:ipsec_callback: " "existing Tunnel Descriptor Block not found (this is good) for SA: %s, %s-bound, allocating.\n", sa_len ? sa : " (error)", (tdbp->tdb_flags & EMT_INBOUND) ? "in" : "out"); /* XXX tdbp->tdb_rcvif = &(enc_softc[em->em_if].enc_if);*/ tdbp->tdb_rcvif = NULL; } else { KLIPS_PRINT(debug_netlink & DB_NL_TDBCB, "klips_debug:ipsec_callback: " "EMT_SETSPI found an old Tunnel Descriptor Block for SA: %s, delete it first.\n", sa_len ? sa : " (error)"); SENDERR(EEXIST); } if ((error = tdb_init(tdbp, em))) { KLIPS_PRINT(debug_netlink & DB_NL_TDBCB, "klips_debug:ipsec_callback: " "EMT_SETSPI not successful for SA: %s, deleting.\n", sa_len ? sa : " (error)"); ipsec_tdbwipe(tdbp); SENDERR(-error); } tdbp->tdb_lifetime_addtime_c = jiffies/HZ; tdbp->tdb_state = 1; if(!tdbp->tdb_lifetime_allocations_c) { tdbp->tdb_lifetime_allocations_c += 1; } puttdb(tdbp); KLIPS_PRINT(debug_netlink & DB_NL_TDBCB, "klips_debug:ipsec_callback: " "EMT_SETSPI successful for SA: %s\n", sa_len ? sa : " (error)"); break; case EMT_DELSPI: if (em->em_if >= 5) /* XXX -- why 5? */ SENDERR(ENODEV); spin_lock_bh(&tdb_lock); tdbp = gettdb(&(em->em_said)); if (tdbp == NULL) { KLIPS_PRINT(debug_netlink & DB_NL_TDBCB, "klips_debug:ipsec_callback: " "EMT_DELSPI Tunnel Descriptor Block not found for SA%s, could not delete.\n", sa_len ? sa : " (error)"); spin_unlock_bh(&tdb_lock); SENDERR(ENXIO); /* XXX -- wrong error message... */ } else { if((error = deltdbchain(tdbp))) { spin_unlock_bh(&tdb_lock); SENDERR(-error); } } spin_unlock_bh(&tdb_lock); break; case EMT_GRPSPIS: nspis = (len - EMT_GRPSPIS_FLEN) / sizeof(em->em_rel[0]); if ((nspis * (sizeof(em->em_rel[0]))) != (len - EMT_GRPSPIS_FLEN)) { printk("klips_debug:ipsec_callback: " "EMT_GRPSPI message size incorrect, expected nspis(%d)*%d, got %d.\n", nspis, sizeof(em->em_rel[0]), (len - EMT_GRPSPIS_FLEN)); SENDERR(EINVAL); break; } spin_lock_bh(&tdb_lock); for (i = 0; i < nspis; i++) { KLIPS_PRINT(debug_netlink, "klips_debug:ipsec_callback: " "EMT_GRPSPI for SA(%d) %s,\n", i, sa_len ? sa : " (error)"); if ((tdbp = gettdb(&(em->em_rel[i].emr_said))) == NULL) { KLIPS_PRINT(debug_netlink, "klips_debug:ipsec_callback: " "EMT_GRPSPI Tunnel Descriptor Block not found for SA%s, could not group.\n", sa_len ? sa : " (error)"); spin_unlock_bh(&tdb_lock); SENDERR(ENXIO); } else { if(tdbp->tdb_inext || tdbp->tdb_onext) { KLIPS_PRINT(debug_netlink, "klips_debug:ipsec_callback: " "EMT_GRPSPI Tunnel Descriptor Block already grouped for SA: %s, can't regroup.\n", sa_len ? sa : " (error)"); spin_unlock_bh(&tdb_lock); SENDERR(EBUSY); } em->em_rel[i].emr_tdb = tdbp; } } tprev = em->em_rel[0].emr_tdb; tprev->tdb_inext = NULL; for (i = 1; i < nspis; i++) { tdbp = em->em_rel[i].emr_tdb; tprev->tdb_onext = tdbp; tdbp->tdb_inext = tprev; tprev = tdbp; } tprev->tdb_onext = NULL; spin_unlock_bh(&tdb_lock); error = 0; break; case EMT_UNGRPSPIS: if (len != (8 + (sizeof(struct sa_id) + sizeof(struct tdb *)) /* 12 */) ) { printk("klips_debug:ipsec_callback: " "EMT_UNGRPSPIS message size incorrect, expected %d, got %d.\n", 8 + (sizeof(struct sa_id) + sizeof(struct tdb *)), len); SENDERR(EINVAL); break; } spin_lock_bh(&tdb_lock); if ((tdbp = gettdb(&(em->em_rel[0].emr_said))) == NULL) { KLIPS_PRINT(debug_netlink, "klips_debug:ipsec_callback: " "EMT_UGRPSPI Tunnel Descriptor Block not found for SA%s, could not ungroup.\n", sa_len ? sa : " (error)"); spin_unlock_bh(&tdb_lock); SENDERR(ENXIO); } while(tdbp->tdb_onext) { tdbp = tdbp->tdb_onext; } while(tdbp->tdb_inext) { tprev = tdbp; tdbp = tdbp->tdb_inext; tprev->tdb_inext = NULL; tdbp->tdb_onext = NULL; } spin_unlock_bh(&tdb_lock); break; case EMT_CLRSPIS: KLIPS_PRINT(debug_netlink, "klips_debug:ipsec_callback: " "spi clear called.\n"); if (em->em_if >= 5) /* XXX -- why 5? */ SENDERR(ENODEV); ipsec_tdbcleanup(0); break; default: KLIPS_PRINT(debug_netlink, "klips_debug:ipsec_callback: " "unknown message type\n"); SENDERR(EINVAL); }
int ip_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) { struct iphdr *iph = skb->h.iph; struct sock *raw_sk=NULL; unsigned char hash; unsigned char flag = 0; struct inet_protocol *ipprot; int brd=IS_MYADDR; struct options * opt = NULL; int is_frag=0; __u32 daddr; #ifdef CONFIG_FIREWALL int fwres; __u16 rport; #endif #ifdef CONFIG_IP_MROUTE int mroute_pkt=0; #endif #ifdef CONFIG_NET_IPV6 /* * Intercept IPv6 frames. We dump ST-II and invalid types just below.. */ if(iph->version == 6) return ipv6_rcv(skb,dev,pt); #endif ip_statistics.IpInReceives++; /* * Tag the ip header of this packet so we can find it */ skb->ip_hdr = iph; /* * RFC1122: 3.1.2.2 MUST silently discard any IP frame that fails the checksum. * RFC1122: 3.1.2.3 MUST discard a frame with invalid source address [NEEDS FIXING]. * * Is the datagram acceptable? * * 1. Length at least the size of an ip header * 2. Version of 4 * 3. Checksums correctly. [Speed optimisation for later, skip loopback checksums] * 4. Doesn't have a bogus length * (5. We ought to check for IP multicast addresses and undefined types.. does this matter ?) */ if (skb->len<sizeof(struct iphdr) || iph->ihl<5 || iph->version != 4 || ip_fast_csum((unsigned char *)iph, iph->ihl) !=0 || skb->len < ntohs(iph->tot_len)) { ip_statistics.IpInHdrErrors++; kfree_skb(skb, FREE_WRITE); return(0); } /* * Our transport medium may have padded the buffer out. Now we know it * is IP we can trim to the true length of the frame. * Note this now means skb->len holds ntohs(iph->tot_len). */ skb_trim(skb,ntohs(iph->tot_len)); if(skb->len < (iph->ihl<<2)) { ip_statistics.IpInHdrErrors++; kfree_skb(skb, FREE_WRITE); return 0; } /* * Account for the packet (even if the packet is * not accepted by the firewall!). We do this after * the sanity checks and the additional ihl check * so we dont account garbage as we might do before. */ #ifdef CONFIG_IP_ACCT ip_fw_chk(iph,dev,NULL,ip_acct_chain,0,IP_FW_MODE_ACCT_IN); #endif /* * Try to select closest <src,dst> alias device, if any. * net_alias_dev_rx32 returns main device if it * fails to found other. * If successful, also incr. alias rx count. * * Only makes sense for unicasts - Thanks ANK. */ #ifdef CONFIG_NET_ALIAS if (skb->pkt_type == PACKET_HOST && iph->daddr != skb->dev->pa_addr && net_alias_has(skb->dev)) { skb->dev = dev = net_alias_dev_rx32(skb->dev, AF_INET, iph->saddr, iph->daddr); } #endif if (iph->ihl > 5) { skb->ip_summed = 0; if (ip_options_compile(NULL, skb)) return(0); opt = (struct options*)skb->proto_priv; #ifdef CONFIG_IP_NOSR if (opt->srr) { kfree_skb(skb, FREE_READ); return -EINVAL; } #endif } #if defined(CONFIG_IP_TRANSPARENT_PROXY) && !defined(CONFIG_IP_ALWAYS_DEFRAG) #define CONFIG_IP_ALWAYS_DEFRAG 1 #endif #ifdef CONFIG_IP_ALWAYS_DEFRAG /* * Defragment all incoming traffic before even looking at it. * If you have forwarding enabled, this makes the system a * defragmenting router. Not a common thing. * You probably DON'T want to enable this unless you have to. * You NEED to use this if you want to use transparent proxying, * otherwise, we can't vouch for your sanity. */ /* * See if the frame is fragmented. */ if(iph->frag_off) { if (iph->frag_off & htons(IP_MF)) is_frag|=IPFWD_FRAGMENT; /* * Last fragment ? */ if (iph->frag_off & htons(IP_OFFSET)) is_frag|=IPFWD_LASTFRAG; /* * Reassemble IP fragments. */ if(is_frag) { /* Defragment. Obtain the complete packet if there is one */ skb=ip_defrag(iph,skb,dev); if(skb==NULL) return 0; skb->dev = dev; iph=skb->h.iph; is_frag = 0; /* * When the reassembled packet gets forwarded, the ip * header checksum should be correct. * For better performance, this should actually only * be done in that particular case, i.e. set a flag * here and calculate the checksum in ip_forward. */ ip_send_check(iph); } } #endif /* * See if the firewall wants to dispose of the packet. */ #ifdef CONFIG_FIREWALL if ((fwres=call_in_firewall(PF_INET, skb->dev, iph, &rport))<FW_ACCEPT) { if(fwres==FW_REJECT) icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0, dev); kfree_skb(skb, FREE_WRITE); return 0; } #ifdef CONFIG_IP_TRANSPARENT_PROXY if (fwres==FW_REDIRECT) skb->redirport = rport; else #endif skb->redirport = 0; #endif #ifndef CONFIG_IP_ALWAYS_DEFRAG /* * Remember if the frame is fragmented. */ if(iph->frag_off) { if (iph->frag_off & htons(IP_MF)) is_frag|=IPFWD_FRAGMENT; /* * Last fragment ? */ if (iph->frag_off & htons(IP_OFFSET)) is_frag|=IPFWD_LASTFRAG; } #endif /* * Do any IP forwarding required. chk_addr() is expensive -- avoid it someday. * * This is inefficient. While finding out if it is for us we could also compute * the routing table entry. This is where the great unified cache theory comes * in as and when someone implements it * * For most hosts over 99% of packets match the first conditional * and don't go via ip_chk_addr. Note: brd is set to IS_MYADDR at * function entry. */ daddr = iph->daddr; #ifdef CONFIG_IP_TRANSPARENT_PROXY /* * ip_chksock adds still more overhead for forwarded traffic... */ if ( iph->daddr == skb->dev->pa_addr || skb->redirport || (brd = ip_chk_addr(iph->daddr)) != 0 || ip_chksock(skb)) #else if ( iph->daddr == skb->dev->pa_addr || (brd = ip_chk_addr(iph->daddr)) != 0) #endif { if (opt && opt->srr) { int srrspace, srrptr; __u32 nexthop; unsigned char * optptr = ((unsigned char *)iph) + opt->srr; if (brd != IS_MYADDR || skb->pkt_type != PACKET_HOST) { kfree_skb(skb, FREE_WRITE); return 0; } for ( srrptr=optptr[2], srrspace = optptr[1]; srrptr <= srrspace; srrptr += 4 ) { int brd2; if (srrptr + 3 > srrspace) { icmp_send(skb, ICMP_PARAMETERPROB, 0, opt->srr+2, skb->dev); kfree_skb(skb, FREE_WRITE); return 0; } memcpy(&nexthop, &optptr[srrptr-1], 4); if ((brd2 = ip_chk_addr(nexthop)) == 0) break; if (brd2 != IS_MYADDR) { /* * ANK: should we implement weak tunneling of multicasts? * Are they obsolete? DVMRP specs (RFC-1075) is old enough... * [They are obsolete] */ kfree_skb(skb, FREE_WRITE); return -EINVAL; } memcpy(&daddr, &optptr[srrptr-1], 4); } if (srrptr <= srrspace) { opt->srr_is_hit = 1; opt->is_changed = 1; if (sysctl_ip_forward) { if (ip_forward(skb, dev, is_frag, nexthop)) kfree_skb(skb, FREE_WRITE); } else { ip_statistics.IpInAddrErrors++; kfree_skb(skb, FREE_WRITE); } return 0; } } #ifdef CONFIG_IP_MULTICAST if(!(dev->flags&IFF_ALLMULTI) && brd==IS_MULTICAST && iph->daddr!=IGMP_ALL_HOSTS && !(dev->flags&IFF_LOOPBACK)) { /* * Check it is for one of our groups */ struct ip_mc_list *ip_mc=dev->ip_mc_list; do { if(ip_mc==NULL) { kfree_skb(skb, FREE_WRITE); return 0; } if(ip_mc->multiaddr==iph->daddr) break; ip_mc=ip_mc->next; } while(1); } #endif #ifndef CONFIG_IP_ALWAYS_DEFRAG /* * Reassemble IP fragments. */ if(is_frag) { /* Defragment. Obtain the complete packet if there is one */ skb=ip_defrag(iph,skb,dev); if(skb==NULL) return 0; skb->dev = dev; iph=skb->h.iph; } #endif #ifdef CONFIG_IP_MASQUERADE /* * Do we need to de-masquerade this packet? */ { int ret = ip_fw_demasquerade(&skb,dev); if (ret < 0) { kfree_skb(skb, FREE_WRITE); return 0; } if (ret) { struct iphdr *iph=skb->h.iph; if (ip_forward(skb, dev, IPFWD_MASQUERADED, iph->daddr)) kfree_skb(skb, FREE_WRITE); return 0; } } #endif /* * Point into the IP datagram, just past the header. */ skb->ip_hdr = iph; skb->h.raw += iph->ihl*4; #ifdef CONFIG_IP_MROUTE /* * Check the state on multicast routing (multicast and not 224.0.0.z) */ if(brd==IS_MULTICAST && (iph->daddr&htonl(0xFFFFFF00))!=htonl(0xE0000000)) mroute_pkt=1; #endif /* * Deliver to raw sockets. This is fun as to avoid copies we want to make no surplus copies. * * RFC 1122: SHOULD pass TOS value up to the transport layer. */ /* Note: See raw.c and net/raw.h, RAWV4_HTABLE_SIZE==MAX_INET_PROTOS */ hash = iph->protocol & (MAX_INET_PROTOS - 1); /* * If there maybe a raw socket we must check - if not we don't care less */ if((raw_sk = raw_v4_htable[hash]) != NULL) { struct sock *sknext = NULL; struct sk_buff *skb1; raw_sk = raw_v4_lookup(raw_sk, iph->protocol, iph->saddr, iph->daddr); if(raw_sk) { /* Any raw sockets */ do { /* Find the next */ sknext = raw_v4_lookup(raw_sk->next, iph->protocol, iph->saddr, iph->daddr); if(sknext) skb1 = skb_clone(skb, GFP_ATOMIC); else break; /* One pending raw socket left */ if(skb1) raw_rcv(raw_sk, skb1, dev, iph->saddr,daddr); raw_sk = sknext; } while(raw_sk!=NULL); /* * Here either raw_sk is the last raw socket, or NULL if none */ /* * We deliver to the last raw socket AFTER the protocol checks as it avoids a surplus copy */ } } /* * skb->h.raw now points at the protocol beyond the IP header. */ for (ipprot = (struct inet_protocol *)inet_protos[hash];ipprot != NULL;ipprot=(struct inet_protocol *)ipprot->next) { struct sk_buff *skb2; if (ipprot->protocol != iph->protocol) continue; /* * See if we need to make a copy of it. This will * only be set if more than one protocol wants it. * and then not for the last one. If there is a pending * raw delivery wait for that */ #ifdef CONFIG_IP_MROUTE if (ipprot->copy || raw_sk || mroute_pkt) #else if (ipprot->copy || raw_sk) #endif { skb2 = skb_clone(skb, GFP_ATOMIC); if(skb2==NULL) continue; } else { skb2 = skb; } flag = 1; /* * Pass on the datagram to each protocol that wants it, * based on the datagram protocol. We should really * check the protocol handler's return values here... */ ipprot->handler(skb2, dev, opt, daddr, (ntohs(iph->tot_len) - (iph->ihl * 4)), iph->saddr, 0, ipprot); } /* * All protocols checked. * If this packet was a broadcast, we may *not* reply to it, since that * causes (proven, grin) ARP storms and a leakage of memory (i.e. all * ICMP reply messages get queued up for transmission...) */ #ifdef CONFIG_IP_MROUTE /* * Forward the last copy to the multicast router. If * there is a pending raw delivery however make a copy * and forward that. */ if(mroute_pkt) { flag=1; if(raw_sk==NULL) ipmr_forward(skb, is_frag); else { struct sk_buff *skb2=skb_clone(skb, GFP_ATOMIC); if(skb2) { skb2->free=1; ipmr_forward(skb2, is_frag); } } } #endif if(raw_sk!=NULL) /* Shift to last raw user */ raw_rcv(raw_sk, skb, dev, iph->saddr, daddr); else if (!flag) /* Free and report errors */ { if (brd != IS_BROADCAST && brd!=IS_MULTICAST) icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, 0, dev); kfree_skb(skb, FREE_WRITE); } return(0); } /* * Do any unicast IP forwarding required. */ /* * Don't forward multicast or broadcast frames. */ if(skb->pkt_type!=PACKET_HOST || brd==IS_BROADCAST) { kfree_skb(skb,FREE_WRITE); return 0; } /* * The packet is for another target. Forward the frame */ if (sysctl_ip_forward) { if (opt && opt->is_strictroute) { icmp_send(skb, ICMP_PARAMETERPROB, 0, 16, skb->dev); kfree_skb(skb, FREE_WRITE); return -1; } if (ip_forward(skb, dev, is_frag, iph->daddr)) kfree_skb(skb, FREE_WRITE); } else { /* printk("Machine %lx tried to use us as a forwarder to %lx but we have forwarding disabled!\n", iph->saddr,iph->daddr);*/ ip_statistics.IpInAddrErrors++; kfree_skb(skb, FREE_WRITE); } return(0); }
int ip_options_echo(struct options * dopt, struct options * sopt, __u32 daddr, __u32 saddr, struct sk_buff * skb) { unsigned char *sptr, *dptr; int soffset, doffset; int optlen; memset(dopt, 0, sizeof(struct options)); dopt->is_data = 1; if (!sopt) sopt = (struct options*)skb->proto_priv; if (sopt->optlen == 0) { dopt->optlen = 0; return 0; } sptr = (sopt->is_data ? sopt->__data - sizeof(struct iphdr) : (unsigned char *)skb->ip_hdr); dptr = dopt->__data; if (sopt->rr) { optlen = sptr[sopt->rr+1]; soffset = sptr[sopt->rr+2]; dopt->rr = dopt->optlen + sizeof(struct iphdr); memcpy(dptr, sptr+sopt->rr, optlen); if (sopt->rr_needaddr && soffset <= optlen) { if (soffset + 3 > optlen) return -EINVAL; dptr[2] = soffset + 4; dopt->rr_needaddr = 1; } dptr += optlen; dopt->optlen += optlen; } if (sopt->ts) { optlen = sptr[sopt->ts+1]; soffset = sptr[sopt->ts+2]; dopt->ts = dopt->optlen + sizeof(struct iphdr); memcpy(dptr, sptr+sopt->ts, optlen); if (soffset <= optlen) { if (sopt->ts_needaddr) { if (soffset + 3 > optlen) return -EINVAL; dopt->ts_needaddr = 1; soffset += 4; } if (sopt->ts_needtime) { if (soffset + 3 > optlen) return -EINVAL; dopt->ts_needtime = 1; soffset += 4; } if (((struct timestamp*)(dptr+1))->flags == IPOPT_TS_PRESPEC) { __u32 addr; memcpy(&addr, sptr+soffset-9, 4); if (ip_chk_addr(addr) == 0) { dopt->ts_needtime = 0; dopt->ts_needaddr = 0; soffset -= 8; } } dptr[2] = soffset; } dptr += optlen; dopt->optlen += optlen; } if (sopt->srr) { unsigned char * start = sptr+sopt->srr; __u32 faddr; optlen = start[1]; soffset = start[2]; doffset = 0; if (soffset > optlen) soffset = optlen + 1; soffset -= 4; if (soffset > 3) { memcpy(&faddr, &start[soffset-1], 4); for (soffset-=4, doffset=4; soffset > 3; soffset-=4, doffset+=4) memcpy(&dptr[doffset-1], &start[soffset-1], 4); /* * RFC1812 requires to fix illegal source routes. */ if (memcmp(&saddr, &start[soffset+3], 4) == 0) doffset -= 4; } if (doffset > 3) { memcpy(&start[doffset-1], &daddr, 4); dopt->faddr = faddr; dptr[0] = start[0]; dptr[1] = doffset+3; dptr[2] = 4; dptr += doffset+3; dopt->srr = dopt->optlen + sizeof(struct iphdr); dopt->optlen += doffset+3; dopt->is_strictroute = sopt->is_strictroute; } } while (dopt->optlen & 3) { *dptr++ = IPOPT_END; dopt->optlen++; } return 0; }
int ip_options_compile(struct options * opt, struct sk_buff * skb) { int l; unsigned char * iph; unsigned char * optptr; int optlen; unsigned char * pp_ptr = NULL; if (!opt) { opt = (struct options*)skb->proto_priv; memset(opt, 0, sizeof(struct options)); iph = (unsigned char*)skb->ip_hdr; opt->optlen = ((struct iphdr *)iph)->ihl*4 - sizeof(struct iphdr); optptr = iph + sizeof(struct iphdr); opt->is_data = 0; } else { optptr = opt->is_data ? opt->__data : (unsigned char*)&skb->ip_hdr[1]; iph = optptr - sizeof(struct iphdr); } for (l = opt->optlen; l > 0; ) { switch (*optptr) { case IPOPT_END: for (optptr++, l--; l>0; l--) { if (*optptr != IPOPT_END) { *optptr = IPOPT_END; opt->is_changed = 1; } } goto eol; case IPOPT_NOOP: l--; optptr++; continue; } optlen = optptr[1]; if (optlen<2 || optlen>l) { pp_ptr = optptr; goto error; } switch (*optptr) { case IPOPT_SSRR: case IPOPT_LSRR: if (optlen < 3) { pp_ptr = optptr + 1; goto error; } if (optptr[2] < 4) { pp_ptr = optptr + 2; goto error; } /* NB: cf RFC-1812 5.2.4.1 */ if (opt->srr) { pp_ptr = optptr; goto error; } if (!skb) { if (optptr[2] != 4 || optlen < 7 || ((optlen-3) & 3)) { pp_ptr = optptr + 1; goto error; } memcpy(&opt->faddr, &optptr[3], 4); if (optlen > 7) memmove(&optptr[3], &optptr[7], optlen-7); } opt->is_strictroute = (optptr[0] == IPOPT_SSRR); opt->srr = optptr - iph; break; case IPOPT_RR: if (opt->rr) { pp_ptr = optptr; goto error; } if (optlen < 3) { pp_ptr = optptr + 1; goto error; } if (optptr[2] < 4) { pp_ptr = optptr + 2; goto error; } if (optptr[2] <= optlen) { if (optptr[2]+3 > optlen) { pp_ptr = optptr + 2; goto error; } if (skb) { memcpy(&optptr[optptr[2]-1], &skb->dev->pa_addr, 4); opt->is_changed = 1; } optptr[2] += 4; opt->rr_needaddr = 1; } opt->rr = optptr - iph; break; case IPOPT_TIMESTAMP: if (opt->ts) { pp_ptr = optptr; goto error; } if (optlen < 4) { pp_ptr = optptr + 1; goto error; } if (optptr[2] < 5) { pp_ptr = optptr + 2; goto error; } if (optptr[2] <= optlen) { struct timestamp * ts = (struct timestamp*)(optptr+1); __u32 * timeptr = NULL; if (ts->ptr+3 > ts->len) { pp_ptr = optptr + 2; goto error; } switch (ts->flags) { case IPOPT_TS_TSONLY: opt->ts = optptr - iph; if (skb) timeptr = (__u32*)&optptr[ts->ptr-1]; opt->ts_needtime = 1; ts->ptr += 4; break; case IPOPT_TS_TSANDADDR: if (ts->ptr+7 > ts->len) { pp_ptr = optptr + 2; goto error; } opt->ts = optptr - iph; if (skb) { memcpy(&optptr[ts->ptr-1], &skb->dev->pa_addr, 4); timeptr = (__u32*)&optptr[ts->ptr+3]; } opt->ts_needaddr = 1; opt->ts_needtime = 1; ts->ptr += 8; break; case IPOPT_TS_PRESPEC: if (ts->ptr+7 > ts->len) { pp_ptr = optptr + 2; goto error; } opt->ts = optptr - iph; { __u32 addr; memcpy(&addr, &optptr[ts->ptr-1], 4); if (ip_chk_addr(addr) == 0) break; if (skb) timeptr = (__u32*)&optptr[ts->ptr+3]; } opt->ts_needaddr = 1; opt->ts_needtime = 1; ts->ptr += 8; break; default: pp_ptr = optptr + 3; goto error; } if (timeptr) { struct timeval tv; __u32 midtime; do_gettimeofday(&tv); midtime = htonl((tv.tv_sec % 86400) * 1000 + tv.tv_usec / 1000); memcpy(timeptr, &midtime, sizeof(__u32)); opt->is_changed = 1; } } else { struct timestamp * ts = (struct timestamp*)(optptr+1); if (ts->overflow == 15) { pp_ptr = optptr + 3; goto error; } opt->ts = optptr - iph; if (skb) { ts->overflow++; opt->is_changed = 1; } } break; case IPOPT_SEC: case IPOPT_SID: default: if (!skb) { pp_ptr = optptr; goto error; } break; } l -= optlen; optptr += optlen; } eol: if (!pp_ptr) return 0; error: if (skb) { icmp_send(skb, ICMP_PARAMETERPROB, 0, pp_ptr-iph, skb->dev); kfree_skb(skb, FREE_READ); } return -EINVAL; }
int arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) { /* * We shouldn't use this type conversion. Check later. */ struct arphdr *arp = (struct arphdr *)skb->h.raw; unsigned char *arp_ptr= (unsigned char *)(arp+1); struct arp_table *entry; struct arp_table *proxy_entry; int addr_hint,hlen,htype; unsigned long hash; unsigned char ha[MAX_ADDR_LEN]; /* So we can enable ints again. */ long sip,tip; unsigned char *sha,*tha; /* * The hardware length of the packet should match the hardware length * of the device. Similarly, the hardware types should match. The * device should be ARP-able. Also, if pln is not 4, then the lookup * is not from an IP number. We can't currently handle this, so toss * it. */ if (arp->ar_hln != dev->addr_len || dev->type != ntohs(arp->ar_hrd) || dev->flags & IFF_NOARP || arp->ar_pln != 4) { kfree_skb(skb, FREE_READ); return 0; } /* * Another test. * The logic here is that the protocol being looked up by arp should * match the protocol the device speaks. If it doesn't, there is a * problem, so toss the packet. */ switch(dev->type) { #ifdef CONFIG_AX25 case ARPHRD_AX25: if(arp->ar_pro != htons(AX25_P_IP)) { kfree_skb(skb, FREE_READ); return 0; } break; #endif case ARPHRD_ETHER: case ARPHRD_ARCNET: if(arp->ar_pro != htons(ETH_P_IP)) { kfree_skb(skb, FREE_READ); return 0; } break; default: printk("ARP: dev->type mangled!\n"); kfree_skb(skb, FREE_READ); return 0; } /* * Extract fields */ hlen = dev->addr_len; htype = dev->type; sha=arp_ptr; arp_ptr+=hlen; memcpy(&sip,arp_ptr,4); arp_ptr+=4; tha=arp_ptr; arp_ptr+=hlen; memcpy(&tip,arp_ptr,4); /* * Check for bad requests for 127.0.0.1. If this is one such, delete it. */ if(tip == INADDR_LOOPBACK) { kfree_skb(skb, FREE_READ); return 0; } /* * Process entry. The idea here is we want to send a reply if it is a * request for us or if it is a request for someone else that we hold * a proxy for. We want to add an entry to our cache if it is a reply * to us or if it is a request for our address. * (The assumption for this last is that if someone is requesting our * address, they are probably intending to talk to us, so it saves time * if we cache their address. Their address is also probably not in * our cache, since ours is not in their cache.) * * Putting this another way, we only care about replies if they are to * us, in which case we add them to the cache. For requests, we care * about those for us and those for our proxies. We reply to both, * and in the case of requests for us we add the requester to the arp * cache. */ addr_hint = ip_chk_addr(tip); if(arp->ar_op == htons(ARPOP_REPLY)) { if(addr_hint!=IS_MYADDR) { /* * Replies to other machines get tossed. */ kfree_skb(skb, FREE_READ); return 0; } /* * Fall through to code below that adds sender to cache. */ } else { /* * It is now an arp request */ /* * Only reply for the real device address or when it's in our proxy tables */ if(tip!=dev->pa_addr) { /* * To get in here, it is a request for someone else. We need to * check if that someone else is one of our proxies. If it isn't, * we can toss it. */ cli(); for(proxy_entry=arp_tables[PROXY_HASH]; proxy_entry; proxy_entry = proxy_entry->next) { /* we will respond to a proxy arp request if the masked arp table ip matches the masked tip. This allows a single proxy arp table entry to be used on a gateway machine to handle all requests for a whole network, rather than having to use a huge number of proxy arp entries and having to keep them uptodate. */ if (proxy_entry->dev != dev && proxy_entry->htype == htype && !((proxy_entry->ip^tip)&proxy_entry->mask)) break; } if (proxy_entry) { memcpy(ha, proxy_entry->ha, hlen); sti(); arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,ha); kfree_skb(skb, FREE_READ); return 0; } else { sti(); kfree_skb(skb, FREE_READ); return 0; } } else { /* * To get here, it must be an arp request for us. We need to reply. */ arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,dev->dev_addr); } } /* * Now all replies are handled. Next, anything that falls through to here * needs to be added to the arp cache, or have its entry updated if it is * there. */ hash = HASH(sip); cli(); for(entry=arp_tables[hash];entry;entry=entry->next) if(entry->ip==sip && entry->htype==htype) break; if(entry) { /* * Entry found; update it. */ memcpy(entry->ha, sha, hlen); entry->hlen = hlen; entry->last_used = jiffies; if (!(entry->flags & ATF_COM)) { /* * This entry was incomplete. Delete the retransmit timer * and switch to complete status. */ del_timer(&entry->timer); entry->flags |= ATF_COM; sti(); /* * Send out waiting packets. We might have problems, if someone is * manually removing entries right now -- entry might become invalid * underneath us. */ arp_send_q(entry, sha); } else { sti(); } } else { /* * No entry found. Need to add a new entry to the arp table. */ entry = (struct arp_table *)kmalloc(sizeof(struct arp_table),GFP_ATOMIC); if(entry == NULL) { sti(); printk("ARP: no memory for new arp entry\n"); kfree_skb(skb, FREE_READ); return 0; } entry->mask = DEF_ARP_NETMASK; entry->ip = sip; entry->hlen = hlen; entry->htype = htype; entry->flags = ATF_COM; init_timer(&entry->timer); memcpy(entry->ha, sha, hlen); entry->last_used = jiffies; entry->dev = skb->dev; skb_queue_head_init(&entry->skb); entry->next = arp_tables[hash]; arp_tables[hash] = entry; sti(); } /* * Replies have been sent, and entries have been added. All done. */ kfree_skb(skb, FREE_READ); return 0; }