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); }
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_setsockopt(struct sock *sk, int level, int optname, char *optval, int optlen) { int val,err; unsigned char ucval; #if defined(CONFIG_IP_FIREWALL) || defined(CONFIG_IP_ACCT) struct ip_fw tmp_fw; #endif if (optval == NULL) { val=0; ucval=0; } else { err=verify_area(VERIFY_READ, optval, sizeof(int)); if(err) return err; val = get_user((int *) optval); ucval=get_user((unsigned char *) optval); } if(level!=SOL_IP) return -EOPNOTSUPP; #ifdef CONFIG_IP_MROUTE if(optname>=MRT_BASE && optname <=MRT_BASE+10) { return ip_mroute_setsockopt(sk,optname,optval,optlen); } #endif switch(optname) { case IP_OPTIONS: { struct options * opt = NULL; struct options * old_opt; if (optlen > 40 || optlen < 0) return -EINVAL; err = verify_area(VERIFY_READ, optval, optlen); if (err) return err; opt = kmalloc(sizeof(struct options)+((optlen+3)&~3), GFP_KERNEL); if (!opt) return -ENOMEM; memset(opt, 0, sizeof(struct options)); if (optlen) memcpy_fromfs(opt->__data, optval, optlen); while (optlen & 3) opt->__data[optlen++] = IPOPT_END; opt->optlen = optlen; opt->is_data = 1; opt->is_setbyuser = 1; if (optlen && ip_options_compile(opt, NULL)) { kfree_s(opt, sizeof(struct options) + optlen); return -EINVAL; } /* * ANK: I'm afraid that receive handler may change * options from under us. */ cli(); old_opt = sk->opt; sk->opt = opt; sti(); if (old_opt) kfree_s(old_opt, sizeof(struct optlen) + old_opt->optlen); return 0; } case IP_TOS: /* This sets both TOS and Precedence */ if (val<0 || val>63) /* Reject setting of unused bits */ return -EINVAL; if ((val&7) > 4 && !suser()) /* Only root can set Prec>4 */ return -EPERM; sk->ip_tos=val; switch (val & 0x38) { case IPTOS_LOWDELAY: sk->priority=SOPRI_INTERACTIVE; break; case IPTOS_THROUGHPUT: sk->priority=SOPRI_BACKGROUND; break; default: sk->priority=SOPRI_NORMAL; break; } return 0; case IP_TTL: if(val<1||val>255) return -EINVAL; sk->ip_ttl=val; return 0; case IP_HDRINCL: if(sk->type!=SOCK_RAW) return -ENOPROTOOPT; sk->ip_hdrincl=val?1:0; return 0; #ifdef CONFIG_IP_MULTICAST case IP_MULTICAST_TTL: { sk->ip_mc_ttl=(int)ucval; return 0; } case IP_MULTICAST_LOOP: { if(ucval!=0 && ucval!=1) return -EINVAL; sk->ip_mc_loop=(int)ucval; return 0; } case IP_MULTICAST_IF: { struct in_addr addr; struct device *dev=NULL; /* * Check the arguments are allowable */ err=verify_area(VERIFY_READ, optval, sizeof(addr)); if(err) return err; memcpy_fromfs(&addr,optval,sizeof(addr)); /* * What address has been requested */ if(addr.s_addr==INADDR_ANY) /* Default */ { sk->ip_mc_name[0]=0; return 0; } /* * Find the device */ dev=ip_mc_find_devfor(addr.s_addr); /* * Did we find one */ if(dev) { strcpy(sk->ip_mc_name,dev->name); return 0; } return -EADDRNOTAVAIL; } case IP_ADD_MEMBERSHIP: { /* * FIXME: Add/Del membership should have a semaphore protecting them from re-entry */ struct ip_mreq mreq; __u32 route_src; struct rtable *rt; struct device *dev=NULL; /* * Check the arguments. */ err=verify_area(VERIFY_READ, optval, sizeof(mreq)); if(err) return err; memcpy_fromfs(&mreq,optval,sizeof(mreq)); /* * Get device for use later */ if(mreq.imr_interface.s_addr==INADDR_ANY) { /* * Not set so scan. */ if((rt=ip_rt_route(mreq.imr_multiaddr.s_addr,0))!=NULL) { dev=rt->rt_dev; route_src = rt->rt_src; atomic_dec(&rt->rt_use); ip_rt_put(rt); } } else { /* * Find a suitable device. */ dev=ip_mc_find_devfor(mreq.imr_interface.s_addr); } /* * No device, no cookies. */ if(!dev) return -ENODEV; /* * Join group. */ return ip_mc_join_group(sk,dev,mreq.imr_multiaddr.s_addr); } case IP_DROP_MEMBERSHIP: { struct ip_mreq mreq; struct rtable *rt; __u32 route_src; struct device *dev=NULL; /* * Check the arguments */ err=verify_area(VERIFY_READ, optval, sizeof(mreq)); if(err) return err; memcpy_fromfs(&mreq,optval,sizeof(mreq)); /* * Get device for use later */ if(mreq.imr_interface.s_addr==INADDR_ANY) { if((rt=ip_rt_route(mreq.imr_multiaddr.s_addr,0))!=NULL) { dev=rt->rt_dev; atomic_dec(&rt->rt_use); route_src = rt->rt_src; ip_rt_put(rt); } } else { dev=ip_mc_find_devfor(mreq.imr_interface.s_addr); } /* * Did we find a suitable device. */ if(!dev) return -ENODEV; /* * Leave group */ return ip_mc_leave_group(sk,dev,mreq.imr_multiaddr.s_addr); } #endif #ifdef CONFIG_IP_FIREWALL case IP_FW_INSERT_IN: case IP_FW_INSERT_OUT: case IP_FW_INSERT_FWD: case IP_FW_APPEND_IN: case IP_FW_APPEND_OUT: case IP_FW_APPEND_FWD: case IP_FW_DELETE_IN: case IP_FW_DELETE_OUT: case IP_FW_DELETE_FWD: case IP_FW_CHECK_IN: case IP_FW_CHECK_OUT: case IP_FW_CHECK_FWD: case IP_FW_FLUSH_IN: case IP_FW_FLUSH_OUT: case IP_FW_FLUSH_FWD: case IP_FW_ZERO_IN: case IP_FW_ZERO_OUT: case IP_FW_ZERO_FWD: case IP_FW_POLICY_IN: case IP_FW_POLICY_OUT: case IP_FW_POLICY_FWD: case IP_FW_MASQ_TIMEOUTS: if(!suser()) return -EPERM; if(optlen>sizeof(tmp_fw) || optlen<1) return -EINVAL; err=verify_area(VERIFY_READ,optval,optlen); if(err) return err; memcpy_fromfs(&tmp_fw,optval,optlen); err=ip_fw_ctl(optname, &tmp_fw,optlen); return -err; /* -0 is 0 after all */ #endif #ifdef CONFIG_IP_ACCT case IP_ACCT_INSERT: case IP_ACCT_APPEND: case IP_ACCT_DELETE: case IP_ACCT_FLUSH: case IP_ACCT_ZERO: if(!suser()) return -EPERM; if(optlen>sizeof(tmp_fw) || optlen<1) return -EINVAL; err=verify_area(VERIFY_READ,optval,optlen); if(err) return err; memcpy_fromfs(&tmp_fw, optval,optlen); err=ip_acct_ctl(optname, &tmp_fw,optlen); return -err; /* -0 is 0 after all */ #endif /* IP_OPTIONS and friends go here eventually */ default: return(-ENOPROTOOPT); } }
/* * This routine builds the appropriate hardware/IP headers for * the routine. It assumes that if *dev != NULL then the * protocol knows what it's doing, otherwise it uses the * routing/ARP tables to select a device struct. */ int ip_build_header(struct sk_buff *skb, __u32 saddr, __u32 daddr, struct device **dev, int type, struct options *opt, int len, int tos, int ttl, struct rtable ** rp) { struct rtable *rt; __u32 raddr; int tmp; struct iphdr *iph; __u32 final_daddr = daddr; if (opt && opt->srr) daddr = opt->faddr; /* * See if we need to look up the device. */ #ifdef CONFIG_IP_MULTICAST if(MULTICAST(daddr) && *dev==NULL && skb->sk && *skb->sk->ip_mc_name) *dev=dev_get(skb->sk->ip_mc_name); #endif if (rp) { rt = ip_check_route(rp, daddr, skb->localroute, *dev); /* * If rp != NULL rt_put following below should not * release route, so that... */ if (rt) atomic_inc(&rt->rt_refcnt); } else rt = ip_rt_route(daddr, skb->localroute, *dev); if (*dev == NULL) { if (rt == NULL) { ip_statistics.IpOutNoRoutes++; return(-ENETUNREACH); } *dev = rt->rt_dev; } if ((LOOPBACK(saddr) && !LOOPBACK(daddr)) || !saddr) saddr = rt ? rt->rt_src : (*dev)->pa_addr; raddr = rt ? rt->rt_gateway : daddr; if (opt && opt->is_strictroute && rt && (rt->rt_flags & RTF_GATEWAY)) { ip_rt_put(rt); ip_statistics.IpOutNoRoutes++; return -ENETUNREACH; } /* * Now build the MAC header. */ if (type==IPPROTO_TCP) tmp = ip_send_room(rt, skb, raddr, len, *dev, saddr); else tmp = ip_send(rt, skb, raddr, len, *dev, saddr); ip_rt_put(rt); /* * Book keeping */ skb->dev = *dev; skb->saddr = saddr; /* * Now build the IP header. */ /* * If we are using IPPROTO_RAW, then we don't need an IP header, since * one is being supplied to us by the user */ if(type == IPPROTO_RAW) return (tmp); /* * Build the IP addresses */ if (opt) iph=(struct iphdr *)skb_put(skb,sizeof(struct iphdr) + opt->optlen); else iph=(struct iphdr *)skb_put(skb,sizeof(struct iphdr)); iph->version = 4; iph->ihl = 5; iph->tos = tos; iph->frag_off = 0; iph->ttl = ttl; iph->daddr = daddr; iph->saddr = saddr; iph->protocol = type; skb->ip_hdr = iph; if (!opt || !opt->optlen) return sizeof(struct iphdr) + tmp; iph->ihl += opt->optlen>>2; ip_options_build(skb, opt, final_daddr, (*dev)->pa_addr, 0); return iph->ihl*4 + tmp; }
/* * This routine builds the appropriate hardware/IP headers for * the routine. It assumes that if *dev != NULL then the * protocol knows what it's doing, otherwise it uses the * routing/ARP tables to select a device struct. */ int ip_build_header(struct sk_buff *skb, unsigned long saddr, unsigned long daddr, struct device **dev, int type, struct options *opt, int len, int tos, int ttl) { static struct options optmem; struct iphdr *iph; struct rtable *rt; unsigned char *buff; unsigned long raddr; int tmp; unsigned long src; /* * If there is no 'from' address as yet, then make it our loopback */ if (saddr == 0) saddr = ip_my_addr(); buff = skb->data; /* * See if we need to look up the device. */ if (*dev == NULL) { if(skb->localroute) rt = ip_rt_local(daddr, &optmem, &src); else rt = ip_rt_route(daddr, &optmem, &src); if (rt == NULL) { ip_statistics.IpOutNoRoutes++; return(-ENETUNREACH); } *dev = rt->rt_dev; /* * If the frame is from us and going off machine it MUST MUST MUST * have the output device ip address and never the loopback */ if (LOOPBACK(saddr) && !LOOPBACK(daddr)) saddr = src;/*rt->rt_dev->pa_addr;*/ raddr = rt->rt_gateway; opt = &optmem; } else { /* * We still need the address of the first hop. */ if(skb->localroute) rt = ip_rt_local(daddr, &optmem, &src); else rt = ip_rt_route(daddr, &optmem, &src); /* * If the frame is from us and going off machine it MUST MUST MUST * have the output device ip address and never the loopback */ if (LOOPBACK(saddr) && !LOOPBACK(daddr)) saddr = src;/*rt->rt_dev->pa_addr;*/ raddr = (rt == NULL) ? 0 : rt->rt_gateway; } /* * No gateway so aim at the real destination */ if (raddr == 0) raddr = daddr; /* * Now build the MAC header. */ tmp = ip_send(skb, raddr, len, *dev, saddr); buff += tmp; len -= tmp; /* * Book keeping */ skb->dev = *dev; skb->saddr = saddr; if (skb->sk) skb->sk->saddr = saddr; /* * Now build the IP header. */ /* * If we are using IPPROTO_RAW, then we don't need an IP header, since * one is being supplied to us by the user */ if(type == IPPROTO_RAW) return (tmp); iph = (struct iphdr *)buff; iph->version = 4; iph->tos = tos; iph->frag_off = 0; iph->ttl = ttl; iph->daddr = daddr; iph->saddr = saddr; iph->protocol = type; iph->ihl = 5; /* Setup the IP options. */ #ifdef Not_Yet_Avail build_options(iph, opt); #endif return(20 + tmp); /* IP header plus MAC header size */ }
static void rt_cache_add(unsigned hash, struct rtable * rth) { unsigned long flags; struct rtable **rthp; __u32 daddr = rth->rt_dst; unsigned long now = jiffies; #if RT_CACHE_DEBUG >= 2 if (ip_rt_lock != 1) { printk("rt_cache_add: ip_rt_lock==%d\n", ip_rt_lock); return; } #endif save_flags(flags); if (rth->rt_dev->header_cache_bind) { struct rtable * rtg = rth; if (rth->rt_gateway != daddr) { ip_rt_fast_unlock(); rtg = ip_rt_route(rth->rt_gateway, 0, NULL); ip_rt_fast_lock(); } if (rtg) { if (rtg == rth) rtg->rt_dev->header_cache_bind(&rtg->rt_hh, rtg->rt_dev, ETH_P_IP, rtg->rt_dst); else { if (rtg->rt_hh) atomic_inc(&rtg->rt_hh->hh_refcnt); rth->rt_hh = rtg->rt_hh; ip_rt_put(rtg); } } } if (rt_cache_size >= RT_CACHE_SIZE_MAX) rt_garbage_collect(); cli(); rth->rt_next = ip_rt_hash_table[hash]; #if RT_CACHE_DEBUG >= 2 if (rth->rt_next) { struct rtable * trth; printk("rt_cache @%02x: %08x", hash, daddr); for (trth=rth->rt_next; trth; trth=trth->rt_next) printk(" . %08x", trth->rt_dst); printk("\n"); } #endif ip_rt_hash_table[hash] = rth; rthp = &rth->rt_next; sti(); rt_cache_size++; /* * Cleanup duplicate (and aged off) entries. */ while ((rth = *rthp) != NULL) { cli(); if ((!rth->rt_refcnt && rth->rt_lastuse + RT_CACHE_TIMEOUT < now) || rth->rt_dst == daddr) { *rthp = rth->rt_next; rt_cache_size--; sti(); #if RT_CACHE_DEBUG >= 2 printk("rt_cache clean %02x@%08x\n", hash, rth->rt_dst); #endif rt_free(rth); continue; } sti(); rthp = &rth->rt_next; } restore_flags(flags); }