Skip to content
Snippets Groups Projects
af_inet.c 32.4 KiB
Newer Older
  • Learn to ignore specific revisions
  • 
    	if (inet->opt && inet->opt->srr)
    		daddr = inet->opt->faddr;
    
    	/* Query new route. */
    	err = ip_route_connect(&rt, daddr, 0,
    			       RT_CONN_FLAGS(sk),
    			       sk->sk_bound_dev_if,
    			       sk->sk_protocol,
    			       inet->sport, inet->dport, sk);
    	if (err)
    		return err;
    
    	sk_setup_caps(sk, &rt->u.dst);
    
    	new_saddr = rt->rt_src;
    
    	if (new_saddr == old_saddr)
    		return 0;
    
    	if (sysctl_ip_dynaddr > 1) {
    		printk(KERN_INFO "%s(): shifting inet->"
    				 "saddr from %d.%d.%d.%d to %d.%d.%d.%d\n",
    		       __FUNCTION__,
    		       NIPQUAD(old_saddr),
    		       NIPQUAD(new_saddr));
    	}
    
    	inet->saddr = inet->rcv_saddr = new_saddr;
    
    	/*
    	 * XXX The only one ugly spot where we need to
    	 * XXX really change the sockets identity after
    	 * XXX it has entered the hashes. -DaveM
    	 *
    	 * Besides that, it does not check for connection
    	 * uniqueness. Wait for troubles.
    	 */
    	__sk_prot_rehash(sk);
    	return 0;
    }
    
    int inet_sk_rebuild_header(struct sock *sk)
    {
    	struct inet_sock *inet = inet_sk(sk);
    	struct rtable *rt = (struct rtable *)__sk_dst_check(sk, 0);
    	u32 daddr;
    	int err;
    
    	/* Route is OK, nothing to do. */
    	if (rt)
    		return 0;
    
    	/* Reroute. */
    	daddr = inet->daddr;
    	if (inet->opt && inet->opt->srr)
    		daddr = inet->opt->faddr;
    {
    	struct flowi fl = {
    		.oif = sk->sk_bound_dev_if,
    		.nl_u = {
    			.ip4_u = {
    				.daddr	= daddr,
    				.saddr	= inet->saddr,
    				.tos	= RT_CONN_FLAGS(sk),
    			},
    		},
    		.proto = sk->sk_protocol,
    		.uli_u = {
    			.ports = {
    				.sport = inet->sport,
    				.dport = inet->dport,
    			},
    		},
    	};
    						
    	err = ip_route_output_flow(&rt, &fl, sk, 0);
    }
    	if (!err)
    		sk_setup_caps(sk, &rt->u.dst);
    	else {
    		/* Routing failed... */
    		sk->sk_route_caps = 0;
    		/*
    		 * Other protocols have to map its equivalent state to TCP_SYN_SENT.
    		 * DCCP maps its DCCP_REQUESTING state to TCP_SYN_SENT. -acme
    		 */
    		if (!sysctl_ip_dynaddr ||
    		    sk->sk_state != TCP_SYN_SENT ||
    		    (sk->sk_userlocks & SOCK_BINDADDR_LOCK) ||
    		    (err = inet_sk_reselect_saddr(sk)) != 0)
    			sk->sk_err_soft = -err;
    	}
    
    	return err;
    }
    
    EXPORT_SYMBOL(inet_sk_rebuild_header);
    
    
    Herbert Xu's avatar
    Herbert Xu committed
    static struct sk_buff *inet_gso_segment(struct sk_buff *skb, int sg)
    {
    	struct sk_buff *segs = ERR_PTR(-EINVAL);
    	struct iphdr *iph;
    	struct net_protocol *ops;
    	int proto;
    	int ihl;
    	int id;
    
    	if (!pskb_may_pull(skb, sizeof(*iph)))
    		goto out;
    
    	iph = skb->nh.iph;
    	ihl = iph->ihl * 4;
    	if (ihl < sizeof(*iph))
    		goto out;
    
    	if (!pskb_may_pull(skb, ihl))
    		goto out;
    
    	skb->h.raw = __skb_pull(skb, ihl);
    	iph = skb->nh.iph;
    	id = ntohs(iph->id);
    	proto = iph->protocol & (MAX_INET_PROTOS - 1);
    	segs = ERR_PTR(-EPROTONOSUPPORT);
    
    	rcu_read_lock();
    	ops = rcu_dereference(inet_protos[proto]);
    	if (ops && ops->gso_segment)
    		segs = ops->gso_segment(skb, sg);
    	rcu_read_unlock();
    
    	if (IS_ERR(segs))
    		goto out;
    
    	skb = segs;
    	do {
    		iph = skb->nh.iph;
    		iph->id = htons(id++);
    		iph->tot_len = htons(skb->len - skb->mac_len);
    		iph->check = 0;
    		iph->check = ip_fast_csum(skb->nh.raw, iph->ihl);
    	} while ((skb = skb->next));
    
    out:
    	return segs;
    }
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    #ifdef CONFIG_IP_MULTICAST
    static struct net_protocol igmp_protocol = {
    	.handler =	igmp_rcv,
    };
    #endif
    
    static struct net_protocol tcp_protocol = {
    	.handler =	tcp_v4_rcv,
    	.err_handler =	tcp_v4_err,
    
    Herbert Xu's avatar
    Herbert Xu committed
    	.gso_segment =	tcp_tso_segment,
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	.no_policy =	1,
    };
    
    static struct net_protocol udp_protocol = {
    	.handler =	udp_rcv,
    	.err_handler =	udp_err,
    	.no_policy =	1,
    };
    
    static struct net_protocol icmp_protocol = {
    	.handler =	icmp_rcv,
    };
    
    static int __init init_ipv4_mibs(void)
    {
    	net_statistics[0] = alloc_percpu(struct linux_mib);
    	net_statistics[1] = alloc_percpu(struct linux_mib);
    	ip_statistics[0] = alloc_percpu(struct ipstats_mib);
    	ip_statistics[1] = alloc_percpu(struct ipstats_mib);
    	icmp_statistics[0] = alloc_percpu(struct icmp_mib);
    	icmp_statistics[1] = alloc_percpu(struct icmp_mib);
    	tcp_statistics[0] = alloc_percpu(struct tcp_mib);
    	tcp_statistics[1] = alloc_percpu(struct tcp_mib);
    	udp_statistics[0] = alloc_percpu(struct udp_mib);
    	udp_statistics[1] = alloc_percpu(struct udp_mib);
    	if (!
    	    (net_statistics[0] && net_statistics[1] && ip_statistics[0]
    	     && ip_statistics[1] && tcp_statistics[0] && tcp_statistics[1]
    	     && udp_statistics[0] && udp_statistics[1]))
    		return -ENOMEM;
    
    	(void) tcp_mib_init();
    
    	return 0;
    }
    
    static int ipv4_proc_init(void);
    
    
    /*
     *	IP protocol layer initialiser
     */
    
    static struct packet_type ip_packet_type = {
    	.type = __constant_htons(ETH_P_IP),
    	.func = ip_rcv,
    
    Herbert Xu's avatar
    Herbert Xu committed
    	.gso_segment = inet_gso_segment,
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    static int __init inet_init(void)
    {
    	struct sk_buff *dummy_skb;
    	struct inet_protosw *q;
    	struct list_head *r;
    	int rc = -EINVAL;
    
    	if (sizeof(struct inet_skb_parm) > sizeof(dummy_skb->cb)) {
    		printk(KERN_CRIT "%s: panic\n", __FUNCTION__);
    		goto out;
    	}
    
    	rc = proto_register(&tcp_prot, 1);
    	if (rc)
    		goto out;
    
    	rc = proto_register(&udp_prot, 1);
    	if (rc)
    		goto out_unregister_tcp_proto;
    
    	rc = proto_register(&raw_prot, 1);
    	if (rc)
    		goto out_unregister_udp_proto;
    
    	/*
    	 *	Tell SOCKET that we are alive... 
    	 */
    
      	(void)sock_register(&inet_family_ops);
    
    	/*
    	 *	Add all the base protocols.
    	 */
    
    	if (inet_add_protocol(&icmp_protocol, IPPROTO_ICMP) < 0)
    		printk(KERN_CRIT "inet_init: Cannot add ICMP protocol\n");
    	if (inet_add_protocol(&udp_protocol, IPPROTO_UDP) < 0)
    		printk(KERN_CRIT "inet_init: Cannot add UDP protocol\n");
    	if (inet_add_protocol(&tcp_protocol, IPPROTO_TCP) < 0)
    		printk(KERN_CRIT "inet_init: Cannot add TCP protocol\n");
    #ifdef CONFIG_IP_MULTICAST
    	if (inet_add_protocol(&igmp_protocol, IPPROTO_IGMP) < 0)
    		printk(KERN_CRIT "inet_init: Cannot add IGMP protocol\n");
    #endif
    
    	/* Register the socket-side information for inet_create. */
    	for (r = &inetsw[0]; r < &inetsw[SOCK_MAX]; ++r)
    		INIT_LIST_HEAD(r);
    
    	for (q = inetsw_array; q < &inetsw_array[INETSW_ARRAY_LEN]; ++q)
    		inet_register_protosw(q);
    
    	/*
    	 *	Set the ARP module up
    	 */
    
    	arp_init();
    
      	/*
      	 *	Set the IP module up
      	 */
    
    	ip_init();
    
    	tcp_v4_init(&inet_family_ops);
    
    	/* Setup TCP slab cache for open requests. */
    	tcp_init();
    
    
    	/*
    	 *	Set the ICMP layer up
    	 */
    
    	icmp_init(&inet_family_ops);
    
    	/*
    	 *	Initialise the multicast router
    	 */
    #if defined(CONFIG_IP_MROUTE)
    	ip_mr_init();
    #endif
    	/*
    	 *	Initialise per-cpu ipv4 mibs
    	 */ 
    
    	if(init_ipv4_mibs())
    		printk(KERN_CRIT "inet_init: Cannot init ipv4 mibs\n"); ;
    	
    	ipv4_proc_init();
    
    	ipfrag_init();
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	rc = 0;
    out:
    	return rc;
    out_unregister_tcp_proto:
    	proto_unregister(&tcp_prot);
    out_unregister_udp_proto:
    	proto_unregister(&udp_prot);
    	goto out;
    }
    
    
    fs_initcall(inet_init);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    /* ------------------------------------------------------------------------ */
    
    #ifdef CONFIG_PROC_FS
    static int __init ipv4_proc_init(void)
    {
    	int rc = 0;
    
    	if (raw_proc_init())
    		goto out_raw;
    	if (tcp4_proc_init())
    		goto out_tcp;
    	if (udp4_proc_init())
    		goto out_udp;
    	if (fib_proc_init())
    		goto out_fib;
    	if (ip_misc_proc_init())
    		goto out_misc;
    out:
    	return rc;
    out_misc:
    	fib_proc_exit();
    out_fib:
    	udp4_proc_exit();
    out_udp:
    	tcp4_proc_exit();
    out_tcp:
    	raw_proc_exit();
    out_raw:
    	rc = -ENOMEM;
    	goto out;
    }
    
    #else /* CONFIG_PROC_FS */
    static int __init ipv4_proc_init(void)
    {
    	return 0;
    }
    #endif /* CONFIG_PROC_FS */
    
    MODULE_ALIAS_NETPROTO(PF_INET);
    
    EXPORT_SYMBOL(inet_accept);
    EXPORT_SYMBOL(inet_bind);
    EXPORT_SYMBOL(inet_dgram_connect);
    EXPORT_SYMBOL(inet_dgram_ops);
    EXPORT_SYMBOL(inet_getname);
    EXPORT_SYMBOL(inet_ioctl);
    EXPORT_SYMBOL(inet_listen);
    EXPORT_SYMBOL(inet_register_protosw);
    EXPORT_SYMBOL(inet_release);
    EXPORT_SYMBOL(inet_sendmsg);
    EXPORT_SYMBOL(inet_shutdown);
    EXPORT_SYMBOL(inet_sock_destruct);
    EXPORT_SYMBOL(inet_stream_connect);
    EXPORT_SYMBOL(inet_stream_ops);
    EXPORT_SYMBOL(inet_unregister_protosw);
    EXPORT_SYMBOL(net_statistics);
    
    EXPORT_SYMBOL(sysctl_ip_nonlocal_bind);