+ #endif /* CONFIG_PROC_FS */
+
+@@ -2508,6 +2540,5 @@
+ EXPORT_SYMBOL(tcp_proc_register);
+ EXPORT_SYMBOL(tcp_proc_unregister);
+ #endif
+-EXPORT_SYMBOL(sysctl_local_port_range);
+ EXPORT_SYMBOL(sysctl_tcp_low_latency);
+
+diff -Nurb linux-2.6.22-570/net/ipv4/tcp_ipv4.c.orig linux-2.6.22-590/net/ipv4/tcp_ipv4.c.orig
+--- linux-2.6.22-570/net/ipv4/tcp_ipv4.c.orig 2008-01-29 22:12:18.000000000 -0500
++++ linux-2.6.22-590/net/ipv4/tcp_ipv4.c.orig 1969-12-31 19:00:00.000000000 -0500
+@@ -1,2483 +0,0 @@
+-/*
+- * INET An implementation of the TCP/IP protocol suite for the LINUX
+- * operating system. INET is implemented using the BSD Socket
+- * interface as the means of communication with the user level.
+- *
+- * Implementation of the Transmission Control Protocol(TCP).
+- *
+- * Version: $Id: tcp_ipv4.c,v 1.240 2002/02/01 22:01:04 davem Exp $
+- *
+- * IPv4 specific functions
+- *
+- *
+- * code split from:
+- * linux/ipv4/tcp.c
+- * linux/ipv4/tcp_input.c
+- * linux/ipv4/tcp_output.c
+- *
+- * See tcp.c for author information
+- *
+- * 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.
+- */
+-
+-/*
+- * Changes:
+- * David S. Miller : New socket lookup architecture.
+- * This code is dedicated to John Dyson.
+- * David S. Miller : Change semantics of established hash,
+- * half is devoted to TIME_WAIT sockets
+- * and the rest go in the other half.
+- * Andi Kleen : Add support for syncookies and fixed
+- * some bugs: ip options weren't passed to
+- * the TCP layer, missed a check for an
+- * ACK bit.
+- * Andi Kleen : Implemented fast path mtu discovery.
+- * Fixed many serious bugs in the
+- * request_sock handling and moved
+- * most of it into the af independent code.
+- * Added tail drop and some other bugfixes.
+- * Added new listen semantics.
+- * Mike McLagan : Routing by source
+- * Juan Jose Ciarlante: ip_dynaddr bits
+- * Andi Kleen: various fixes.
+- * Vitaly E. Lavrov : Transparent proxy revived after year
+- * coma.
+- * Andi Kleen : Fix new listen.
+- * Andi Kleen : Fix accept error reporting.
+- * YOSHIFUJI Hideaki @USAGI and: Support IPV6_V6ONLY socket option, which
+- * Alexey Kuznetsov allow both IPv4 and IPv6 sockets to bind
+- * a single port at the same time.
+- */
+-
+-
+-#include <linux/types.h>
+-#include <linux/fcntl.h>
+-#include <linux/module.h>
+-#include <linux/random.h>
+-#include <linux/cache.h>
+-#include <linux/jhash.h>
+-#include <linux/init.h>
+-#include <linux/times.h>
+-
+-#include <net/icmp.h>
+-#include <net/inet_hashtables.h>
+-#include <net/tcp.h>
+-#include <net/transp_v6.h>
+-#include <net/ipv6.h>
+-#include <net/inet_common.h>
+-#include <net/timewait_sock.h>
+-#include <net/xfrm.h>
+-#include <net/netdma.h>
+-
+-#include <linux/inet.h>
+-#include <linux/ipv6.h>
+-#include <linux/stddef.h>
+-#include <linux/proc_fs.h>
+-#include <linux/seq_file.h>
+-
+-#include <linux/crypto.h>
+-#include <linux/scatterlist.h>
+-
+-int sysctl_tcp_tw_reuse __read_mostly;
+-int sysctl_tcp_low_latency __read_mostly;
+-
+-/* Check TCP sequence numbers in ICMP packets. */
+-#define ICMP_MIN_LENGTH 8
+-
+-/* Socket used for sending RSTs */
+-static struct socket *tcp_socket __read_mostly;
+-
+-void tcp_v4_send_check(struct sock *sk, int len, struct sk_buff *skb);
+-
+-#ifdef CONFIG_TCP_MD5SIG
+-static struct tcp_md5sig_key *tcp_v4_md5_do_lookup(struct sock *sk,
+- __be32 addr);
+-static int tcp_v4_do_calc_md5_hash(char *md5_hash, struct tcp_md5sig_key *key,
+- __be32 saddr, __be32 daddr,
+- struct tcphdr *th, int protocol,
+- int tcplen);
+-#endif
+-
+-struct inet_hashinfo __cacheline_aligned tcp_hashinfo = {
+- .lhash_lock = __RW_LOCK_UNLOCKED(tcp_hashinfo.lhash_lock),
+- .lhash_users = ATOMIC_INIT(0),
+- .lhash_wait = __WAIT_QUEUE_HEAD_INITIALIZER(tcp_hashinfo.lhash_wait),
+-};
+-
+-static int tcp_v4_get_port(struct sock *sk, unsigned short snum)
+-{
+- return inet_csk_get_port(&tcp_hashinfo, sk, snum,
+- inet_csk_bind_conflict);
+-}
+-
+-static void tcp_v4_hash(struct sock *sk)
+-{
+- inet_hash(&tcp_hashinfo, sk);
+-}
+-
+-void tcp_unhash(struct sock *sk)
+-{
+- inet_unhash(&tcp_hashinfo, sk);
+-}
+-
+-static inline __u32 tcp_v4_init_sequence(struct sk_buff *skb)
+-{
+- return secure_tcp_sequence_number(ip_hdr(skb)->daddr,
+- ip_hdr(skb)->saddr,
+- tcp_hdr(skb)->dest,
+- tcp_hdr(skb)->source);
+-}
+-
+-int tcp_twsk_unique(struct sock *sk, struct sock *sktw, void *twp)
+-{
+- const struct tcp_timewait_sock *tcptw = tcp_twsk(sktw);
+- struct tcp_sock *tp = tcp_sk(sk);
+-
+- /* With PAWS, it is safe from the viewpoint
+- of data integrity. Even without PAWS it is safe provided sequence
+- spaces do not overlap i.e. at data rates <= 80Mbit/sec.
+-
+- Actually, the idea is close to VJ's one, only timestamp cache is
+- held not per host, but per port pair and TW bucket is used as state
+- holder.
+-
+- If TW bucket has been already destroyed we fall back to VJ's scheme
+- and use initial timestamp retrieved from peer table.
+- */
+- if (tcptw->tw_ts_recent_stamp &&
+- (twp == NULL || (sysctl_tcp_tw_reuse &&
+- get_seconds() - tcptw->tw_ts_recent_stamp > 1))) {
+- tp->write_seq = tcptw->tw_snd_nxt + 65535 + 2;
+- if (tp->write_seq == 0)
+- tp->write_seq = 1;
+- tp->rx_opt.ts_recent = tcptw->tw_ts_recent;
+- tp->rx_opt.ts_recent_stamp = tcptw->tw_ts_recent_stamp;
+- sock_hold(sktw);
+- return 1;
+- }
+-
+- return 0;
+-}
+-
+-EXPORT_SYMBOL_GPL(tcp_twsk_unique);
+-
+-/* This will initiate an outgoing connection. */
+-int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
+-{
+- struct inet_sock *inet = inet_sk(sk);
+- struct tcp_sock *tp = tcp_sk(sk);
+- struct sockaddr_in *usin = (struct sockaddr_in *)uaddr;
+- struct rtable *rt;
+- __be32 daddr, nexthop;
+- int tmp;
+- int err;
+-
+- if (addr_len < sizeof(struct sockaddr_in))
+- return -EINVAL;
+-
+- if (usin->sin_family != AF_INET)
+- return -EAFNOSUPPORT;
+-
+- nexthop = daddr = usin->sin_addr.s_addr;
+- if (inet->opt && inet->opt->srr) {
+- if (!daddr)
+- return -EINVAL;
+- nexthop = inet->opt->faddr;
+- }
+-
+- tmp = ip_route_connect(&rt, nexthop, inet->saddr,
+- RT_CONN_FLAGS(sk), sk->sk_bound_dev_if,
+- IPPROTO_TCP,
+- inet->sport, usin->sin_port, sk, 1);
+- if (tmp < 0) {
+- if (tmp == -ENETUNREACH)
+- IP_INC_STATS_BH(IPSTATS_MIB_OUTNOROUTES);
+- return tmp;
+- }
+-
+- if (rt->rt_flags & (RTCF_MULTICAST | RTCF_BROADCAST)) {
+- ip_rt_put(rt);
+- return -ENETUNREACH;
+- }
+-
+- if (!inet->opt || !inet->opt->srr)
+- daddr = rt->rt_dst;
+-
+- if (!inet->saddr)
+- inet->saddr = rt->rt_src;
+- inet->rcv_saddr = inet->saddr;
+-
+- if (tp->rx_opt.ts_recent_stamp && inet->daddr != daddr) {
+- /* Reset inherited state */
+- tp->rx_opt.ts_recent = 0;
+- tp->rx_opt.ts_recent_stamp = 0;
+- tp->write_seq = 0;
+- }
+-
+- if (tcp_death_row.sysctl_tw_recycle &&
+- !tp->rx_opt.ts_recent_stamp && rt->rt_dst == daddr) {
+- struct inet_peer *peer = rt_get_peer(rt);
+- /*
+- * VJ's idea. We save last timestamp seen from
+- * the destination in peer table, when entering state
+- * TIME-WAIT * and initialize rx_opt.ts_recent from it,
+- * when trying new connection.
+- */
+- if (peer != NULL &&
+- peer->tcp_ts_stamp + TCP_PAWS_MSL >= get_seconds()) {
+- tp->rx_opt.ts_recent_stamp = peer->tcp_ts_stamp;
+- tp->rx_opt.ts_recent = peer->tcp_ts;
+- }
+- }
+-
+- inet->dport = usin->sin_port;
+- inet->daddr = daddr;
+-
+- inet_csk(sk)->icsk_ext_hdr_len = 0;
+- if (inet->opt)
+- inet_csk(sk)->icsk_ext_hdr_len = inet->opt->optlen;
+-
+- tp->rx_opt.mss_clamp = 536;
+-
+- /* Socket identity is still unknown (sport may be zero).
+- * However we set state to SYN-SENT and not releasing socket
+- * lock select source port, enter ourselves into the hash tables and
+- * complete initialization after this.
+- */
+- tcp_set_state(sk, TCP_SYN_SENT);
+- err = inet_hash_connect(&tcp_death_row, sk);
+- if (err)
+- goto failure;
+-
+- err = ip_route_newports(&rt, IPPROTO_TCP,
+- inet->sport, inet->dport, sk);
+- if (err)
+- goto failure;
+-
+- /* OK, now commit destination to socket. */
+- sk->sk_gso_type = SKB_GSO_TCPV4;
+- sk_setup_caps(sk, &rt->u.dst);
+-
+- if (!tp->write_seq)
+- tp->write_seq = secure_tcp_sequence_number(inet->saddr,
+- inet->daddr,
+- inet->sport,
+- usin->sin_port);
+-
+- inet->id = tp->write_seq ^ jiffies;
+-
+- err = tcp_connect(sk);
+- rt = NULL;
+- if (err)
+- goto failure;
+-
+- return 0;
+-
+-failure:
+- /*
+- * This unhashes the socket and releases the local port,
+- * if necessary.
+- */
+- tcp_set_state(sk, TCP_CLOSE);
+- ip_rt_put(rt);
+- sk->sk_route_caps = 0;
+- inet->dport = 0;
+- return err;
+-}
+-
+-/*
+- * This routine does path mtu discovery as defined in RFC1191.
+- */
+-static void do_pmtu_discovery(struct sock *sk, struct iphdr *iph, u32 mtu)
+-{
+- struct dst_entry *dst;
+- struct inet_sock *inet = inet_sk(sk);
+-
+- /* We are not interested in TCP_LISTEN and open_requests (SYN-ACKs
+- * send out by Linux are always <576bytes so they should go through
+- * unfragmented).
+- */
+- if (sk->sk_state == TCP_LISTEN)
+- return;
+-
+- /* We don't check in the destentry if pmtu discovery is forbidden
+- * on this route. We just assume that no packet_to_big packets
+- * are send back when pmtu discovery is not active.
+- * There is a small race when the user changes this flag in the
+- * route, but I think that's acceptable.
+- */
+- if ((dst = __sk_dst_check(sk, 0)) == NULL)
+- return;
+-
+- dst->ops->update_pmtu(dst, mtu);
+-
+- /* Something is about to be wrong... Remember soft error
+- * for the case, if this connection will not able to recover.
+- */
+- if (mtu < dst_mtu(dst) && ip_dont_fragment(sk, dst))
+- sk->sk_err_soft = EMSGSIZE;
+-
+- mtu = dst_mtu(dst);
+-
+- if (inet->pmtudisc != IP_PMTUDISC_DONT &&
+- inet_csk(sk)->icsk_pmtu_cookie > mtu) {
+- tcp_sync_mss(sk, mtu);
+-
+- /* Resend the TCP packet because it's
+- * clear that the old packet has been
+- * dropped. This is the new "fast" path mtu
+- * discovery.
+- */
+- tcp_simple_retransmit(sk);
+- } /* else let the usual retransmit timer handle it */
+-}
+-
+-/*
+- * This routine is called by the ICMP module when it gets some
+- * sort of error condition. If err < 0 then the socket should
+- * be closed and the error returned to the user. If err > 0
+- * it's just the icmp type << 8 | icmp code. After adjustment
+- * header points to the first 8 bytes of the tcp header. We need
+- * to find the appropriate port.
+- *
+- * The locking strategy used here is very "optimistic". When
+- * someone else accesses the socket the ICMP is just dropped
+- * and for some paths there is no check at all.
+- * A more general error queue to queue errors for later handling
+- * is probably better.
+- *
+- */
+-
+-void tcp_v4_err(struct sk_buff *skb, u32 info)
+-{
+- struct iphdr *iph = (struct iphdr *)skb->data;
+- struct tcphdr *th = (struct tcphdr *)(skb->data + (iph->ihl << 2));
+- struct tcp_sock *tp;
+- struct inet_sock *inet;
+- const int type = icmp_hdr(skb)->type;
+- const int code = icmp_hdr(skb)->code;
+- struct sock *sk;
+- __u32 seq;
+- int err;
+-
+- if (skb->len < (iph->ihl << 2) + 8) {
+- ICMP_INC_STATS_BH(ICMP_MIB_INERRORS);
+- return;
+- }
+-
+- sk = inet_lookup(&tcp_hashinfo, iph->daddr, th->dest, iph->saddr,
+- th->source, inet_iif(skb));
+- if (!sk) {
+- ICMP_INC_STATS_BH(ICMP_MIB_INERRORS);
+- return;
+- }
+- if (sk->sk_state == TCP_TIME_WAIT) {
+- inet_twsk_put(inet_twsk(sk));
+- return;
+- }
+-
+- bh_lock_sock(sk);
+- /* If too many ICMPs get dropped on busy
+- * servers this needs to be solved differently.
+- */
+- if (sock_owned_by_user(sk))
+- NET_INC_STATS_BH(LINUX_MIB_LOCKDROPPEDICMPS);
+-
+- if (sk->sk_state == TCP_CLOSE)
+- goto out;
+-
+- tp = tcp_sk(sk);
+- seq = ntohl(th->seq);
+- if (sk->sk_state != TCP_LISTEN &&
+- !between(seq, tp->snd_una, tp->snd_nxt)) {
+- NET_INC_STATS_BH(LINUX_MIB_OUTOFWINDOWICMPS);
+- goto out;
+- }
+-
+- switch (type) {
+- case ICMP_SOURCE_QUENCH:
+- /* Just silently ignore these. */
+- goto out;
+- case ICMP_PARAMETERPROB:
+- err = EPROTO;
+- break;
+- case ICMP_DEST_UNREACH:
+- if (code > NR_ICMP_UNREACH)
+- goto out;
+-
+- if (code == ICMP_FRAG_NEEDED) { /* PMTU discovery (RFC1191) */
+- if (!sock_owned_by_user(sk))
+- do_pmtu_discovery(sk, iph, info);
+- goto out;
+- }
+-
+- err = icmp_err_convert[code].errno;
+- break;
+- case ICMP_TIME_EXCEEDED:
+- err = EHOSTUNREACH;
+- break;
+- default:
+- goto out;
+- }
+-
+- switch (sk->sk_state) {
+- struct request_sock *req, **prev;
+- case TCP_LISTEN:
+- if (sock_owned_by_user(sk))
+- goto out;
+-
+- req = inet_csk_search_req(sk, &prev, th->dest,
+- iph->daddr, iph->saddr);
+- if (!req)
+- goto out;
+-
+- /* ICMPs are not backlogged, hence we cannot get
+- an established socket here.
+- */
+- BUG_TRAP(!req->sk);
+-
+- if (seq != tcp_rsk(req)->snt_isn) {
+- NET_INC_STATS_BH(LINUX_MIB_OUTOFWINDOWICMPS);
+- goto out;
+- }
+-
+- /*
+- * Still in SYN_RECV, just remove it silently.
+- * There is no good way to pass the error to the newly
+- * created socket, and POSIX does not want network
+- * errors returned from accept().
+- */
+- inet_csk_reqsk_queue_drop(sk, req, prev);
+- goto out;
+-
+- case TCP_SYN_SENT:
+- case TCP_SYN_RECV: /* Cannot happen.
+- It can f.e. if SYNs crossed.
+- */
+- if (!sock_owned_by_user(sk)) {
+- sk->sk_err = err;
+-
+- sk->sk_error_report(sk);
+-
+- tcp_done(sk);
+- } else {
+- sk->sk_err_soft = err;
+- }
+- goto out;
+- }
+-
+- /* If we've already connected we will keep trying
+- * until we time out, or the user gives up.
+- *
+- * rfc1122 4.2.3.9 allows to consider as hard errors
+- * only PROTO_UNREACH and PORT_UNREACH (well, FRAG_FAILED too,
+- * but it is obsoleted by pmtu discovery).
+- *
+- * Note, that in modern internet, where routing is unreliable
+- * and in each dark corner broken firewalls sit, sending random
+- * errors ordered by their masters even this two messages finally lose
+- * their original sense (even Linux sends invalid PORT_UNREACHs)
+- *
+- * Now we are in compliance with RFCs.
+- * --ANK (980905)
+- */
+-
+- inet = inet_sk(sk);
+- if (!sock_owned_by_user(sk) && inet->recverr) {
+- sk->sk_err = err;
+- sk->sk_error_report(sk);
+- } else { /* Only an error on timeout */
+- sk->sk_err_soft = err;
+- }
+-
+-out:
+- bh_unlock_sock(sk);
+- sock_put(sk);
+-}
+-
+-/* This routine computes an IPv4 TCP checksum. */
+-void tcp_v4_send_check(struct sock *sk, int len, struct sk_buff *skb)
+-{
+- struct inet_sock *inet = inet_sk(sk);
+- struct tcphdr *th = tcp_hdr(skb);
+-
+- if (skb->ip_summed == CHECKSUM_PARTIAL) {
+- th->check = ~tcp_v4_check(len, inet->saddr,
+- inet->daddr, 0);
+- skb->csum_start = skb_transport_header(skb) - skb->head;
+- skb->csum_offset = offsetof(struct tcphdr, check);
+- } else {
+- th->check = tcp_v4_check(len, inet->saddr, inet->daddr,
+- csum_partial((char *)th,
+- th->doff << 2,
+- skb->csum));
+- }
+-}
+-
+-int tcp_v4_gso_send_check(struct sk_buff *skb)
+-{
+- const struct iphdr *iph;
+- struct tcphdr *th;
+-
+- if (!pskb_may_pull(skb, sizeof(*th)))
+- return -EINVAL;
+-
+- iph = ip_hdr(skb);
+- th = tcp_hdr(skb);
+-
+- th->check = 0;
+- th->check = ~tcp_v4_check(skb->len, iph->saddr, iph->daddr, 0);
+- skb->csum_start = skb_transport_header(skb) - skb->head;
+- skb->csum_offset = offsetof(struct tcphdr, check);
+- skb->ip_summed = CHECKSUM_PARTIAL;
+- return 0;
+-}
+-
+-/*
+- * This routine will send an RST to the other tcp.
+- *
+- * Someone asks: why I NEVER use socket parameters (TOS, TTL etc.)
+- * for reset.
+- * Answer: if a packet caused RST, it is not for a socket
+- * existing in our system, if it is matched to a socket,
+- * it is just duplicate segment or bug in other side's TCP.
+- * So that we build reply only basing on parameters
+- * arrived with segment.
+- * Exception: precedence violation. We do not implement it in any case.
+- */
+-
+-static void tcp_v4_send_reset(struct sock *sk, struct sk_buff *skb)
+-{
+- struct tcphdr *th = tcp_hdr(skb);
+- struct {
+- struct tcphdr th;
+-#ifdef CONFIG_TCP_MD5SIG
+- __be32 opt[(TCPOLEN_MD5SIG_ALIGNED >> 2)];
+-#endif
+- } rep;
+- struct ip_reply_arg arg;
+-#ifdef CONFIG_TCP_MD5SIG
+- struct tcp_md5sig_key *key;
+-#endif
+-
+- /* Never send a reset in response to a reset. */
+- if (th->rst)
+- return;
+-
+- if (((struct rtable *)skb->dst)->rt_type != RTN_LOCAL)
+- return;
+-
+- /* Swap the send and the receive. */
+- memset(&rep, 0, sizeof(rep));
+- rep.th.dest = th->source;
+- rep.th.source = th->dest;
+- rep.th.doff = sizeof(struct tcphdr) / 4;
+- rep.th.rst = 1;
+-
+- if (th->ack) {
+- rep.th.seq = th->ack_seq;
+- } else {
+- rep.th.ack = 1;
+- rep.th.ack_seq = htonl(ntohl(th->seq) + th->syn + th->fin +
+- skb->len - (th->doff << 2));
+- }
+-
+- memset(&arg, 0, sizeof(arg));
+- arg.iov[0].iov_base = (unsigned char *)&rep;
+- arg.iov[0].iov_len = sizeof(rep.th);
+-
+-#ifdef CONFIG_TCP_MD5SIG
+- key = sk ? tcp_v4_md5_do_lookup(sk, ip_hdr(skb)->daddr) : NULL;
+- if (key) {
+- rep.opt[0] = htonl((TCPOPT_NOP << 24) |
+- (TCPOPT_NOP << 16) |
+- (TCPOPT_MD5SIG << 8) |
+- TCPOLEN_MD5SIG);
+- /* Update length and the length the header thinks exists */
+- arg.iov[0].iov_len += TCPOLEN_MD5SIG_ALIGNED;
+- rep.th.doff = arg.iov[0].iov_len / 4;
+-
+- tcp_v4_do_calc_md5_hash((__u8 *)&rep.opt[1],
+- key,
+- ip_hdr(skb)->daddr,
+- ip_hdr(skb)->saddr,
+- &rep.th, IPPROTO_TCP,
+- arg.iov[0].iov_len);
+- }
+-#endif
+- arg.csum = csum_tcpudp_nofold(ip_hdr(skb)->daddr,
+- ip_hdr(skb)->saddr, /* XXX */
+- sizeof(struct tcphdr), IPPROTO_TCP, 0);
+- arg.csumoffset = offsetof(struct tcphdr, check) / 2;
+-
+- ip_send_reply(tcp_socket->sk, skb, &arg, arg.iov[0].iov_len);
+-
+- TCP_INC_STATS_BH(TCP_MIB_OUTSEGS);
+- TCP_INC_STATS_BH(TCP_MIB_OUTRSTS);
+-}
+-
+-/* The code following below sending ACKs in SYN-RECV and TIME-WAIT states
+- outside socket context is ugly, certainly. What can I do?
+- */
+-
+-static void tcp_v4_send_ack(struct tcp_timewait_sock *twsk,
+- struct sk_buff *skb, u32 seq, u32 ack,
+- u32 win, u32 ts)
+-{
+- struct tcphdr *th = tcp_hdr(skb);
+- struct {
+- struct tcphdr th;
+- __be32 opt[(TCPOLEN_TSTAMP_ALIGNED >> 2)
+-#ifdef CONFIG_TCP_MD5SIG
+- + (TCPOLEN_MD5SIG_ALIGNED >> 2)
+-#endif
+- ];
+- } rep;
+- struct ip_reply_arg arg;
+-#ifdef CONFIG_TCP_MD5SIG
+- struct tcp_md5sig_key *key;
+- struct tcp_md5sig_key tw_key;
+-#endif
+-
+- memset(&rep.th, 0, sizeof(struct tcphdr));
+- memset(&arg, 0, sizeof(arg));
+-
+- arg.iov[0].iov_base = (unsigned char *)&rep;
+- arg.iov[0].iov_len = sizeof(rep.th);
+- if (ts) {
+- rep.opt[0] = htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) |
+- (TCPOPT_TIMESTAMP << 8) |
+- TCPOLEN_TIMESTAMP);
+- rep.opt[1] = htonl(tcp_time_stamp);
+- rep.opt[2] = htonl(ts);
+- arg.iov[0].iov_len += TCPOLEN_TSTAMP_ALIGNED;
+- }
+-
+- /* Swap the send and the receive. */
+- rep.th.dest = th->source;
+- rep.th.source = th->dest;
+- rep.th.doff = arg.iov[0].iov_len / 4;
+- rep.th.seq = htonl(seq);
+- rep.th.ack_seq = htonl(ack);
+- rep.th.ack = 1;
+- rep.th.window = htons(win);
+-
+-#ifdef CONFIG_TCP_MD5SIG
+- /*
+- * The SKB holds an imcoming packet, but may not have a valid ->sk
+- * pointer. This is especially the case when we're dealing with a
+- * TIME_WAIT ack, because the sk structure is long gone, and only
+- * the tcp_timewait_sock remains. So the md5 key is stashed in that
+- * structure, and we use it in preference. I believe that (twsk ||
+- * skb->sk) holds true, but we program defensively.
+- */
+- if (!twsk && skb->sk) {
+- key = tcp_v4_md5_do_lookup(skb->sk, ip_hdr(skb)->daddr);
+- } else if (twsk && twsk->tw_md5_keylen) {
+- tw_key.key = twsk->tw_md5_key;
+- tw_key.keylen = twsk->tw_md5_keylen;
+- key = &tw_key;
+- } else
+- key = NULL;
+-
+- if (key) {
+- int offset = (ts) ? 3 : 0;
+-
+- rep.opt[offset++] = htonl((TCPOPT_NOP << 24) |
+- (TCPOPT_NOP << 16) |
+- (TCPOPT_MD5SIG << 8) |
+- TCPOLEN_MD5SIG);
+- arg.iov[0].iov_len += TCPOLEN_MD5SIG_ALIGNED;
+- rep.th.doff = arg.iov[0].iov_len/4;
+-
+- tcp_v4_do_calc_md5_hash((__u8 *)&rep.opt[offset],
+- key,
+- ip_hdr(skb)->daddr,
+- ip_hdr(skb)->saddr,
+- &rep.th, IPPROTO_TCP,
+- arg.iov[0].iov_len);
+- }
+-#endif
+- arg.csum = csum_tcpudp_nofold(ip_hdr(skb)->daddr,
+- ip_hdr(skb)->saddr, /* XXX */
+- arg.iov[0].iov_len, IPPROTO_TCP, 0);
+- arg.csumoffset = offsetof(struct tcphdr, check) / 2;
+- if (twsk)
+- arg.bound_dev_if = twsk->tw_sk.tw_bound_dev_if;
+-
+- ip_send_reply(tcp_socket->sk, skb, &arg, arg.iov[0].iov_len);
+-
+- TCP_INC_STATS_BH(TCP_MIB_OUTSEGS);
+-}
+-
+-static void tcp_v4_timewait_ack(struct sock *sk, struct sk_buff *skb)
+-{
+- struct inet_timewait_sock *tw = inet_twsk(sk);
+- struct tcp_timewait_sock *tcptw = tcp_twsk(sk);
+-
+- tcp_v4_send_ack(tcptw, skb, tcptw->tw_snd_nxt, tcptw->tw_rcv_nxt,
+- tcptw->tw_rcv_wnd >> tw->tw_rcv_wscale,
+- tcptw->tw_ts_recent);
+-
+- inet_twsk_put(tw);
+-}
+-
+-static void tcp_v4_reqsk_send_ack(struct sk_buff *skb,
+- struct request_sock *req)
+-{
+- tcp_v4_send_ack(NULL, skb, tcp_rsk(req)->snt_isn + 1,
+- tcp_rsk(req)->rcv_isn + 1, req->rcv_wnd,
+- req->ts_recent);
+-}
+-
+-/*
+- * Send a SYN-ACK after having received an ACK.
+- * This still operates on a request_sock only, not on a big
+- * socket.
+- */
+-static int tcp_v4_send_synack(struct sock *sk, struct request_sock *req,
+- struct dst_entry *dst)
+-{
+- const struct inet_request_sock *ireq = inet_rsk(req);
+- int err = -1;
+- struct sk_buff * skb;
+-
+- /* First, grab a route. */
+- if (!dst && (dst = inet_csk_route_req(sk, req)) == NULL)
+- goto out;
+-
+- skb = tcp_make_synack(sk, dst, req);
+-
+- if (skb) {
+- struct tcphdr *th = tcp_hdr(skb);
+-
+- th->check = tcp_v4_check(skb->len,
+- ireq->loc_addr,
+- ireq->rmt_addr,
+- csum_partial((char *)th, skb->len,
+- skb->csum));
+-
+- err = ip_build_and_send_pkt(skb, sk, ireq->loc_addr,
+- ireq->rmt_addr,
+- ireq->opt);
+- err = net_xmit_eval(err);
+- }
+-
+-out:
+- dst_release(dst);
+- return err;
+-}
+-
+-/*
+- * IPv4 request_sock destructor.
+- */
+-static void tcp_v4_reqsk_destructor(struct request_sock *req)
+-{
+- kfree(inet_rsk(req)->opt);
+-}
+-
+-#ifdef CONFIG_SYN_COOKIES
+-static void syn_flood_warning(struct sk_buff *skb)
+-{
+- static unsigned long warntime;
+-
+- if (time_after(jiffies, (warntime + HZ * 60))) {
+- warntime = jiffies;
+- printk(KERN_INFO
+- "possible SYN flooding on port %d. Sending cookies.\n",
+- ntohs(tcp_hdr(skb)->dest));
+- }
+-}
+-#endif
+-
+-/*
+- * Save and compile IPv4 options into the request_sock if needed.
+- */
+-static struct ip_options *tcp_v4_save_options(struct sock *sk,
+- struct sk_buff *skb)
+-{
+- struct ip_options *opt = &(IPCB(skb)->opt);
+- struct ip_options *dopt = NULL;
+-
+- if (opt && opt->optlen) {
+- int opt_size = optlength(opt);
+- dopt = kmalloc(opt_size, GFP_ATOMIC);
+- if (dopt) {
+- if (ip_options_echo(dopt, skb)) {
+- kfree(dopt);
+- dopt = NULL;
+- }
+- }
+- }
+- return dopt;
+-}
+-
+-#ifdef CONFIG_TCP_MD5SIG
+-/*
+- * RFC2385 MD5 checksumming requires a mapping of
+- * IP address->MD5 Key.
+- * We need to maintain these in the sk structure.
+- */
+-
+-/* Find the Key structure for an address. */
+-static struct tcp_md5sig_key *
+- tcp_v4_md5_do_lookup(struct sock *sk, __be32 addr)
+-{
+- struct tcp_sock *tp = tcp_sk(sk);
+- int i;
+-
+- if (!tp->md5sig_info || !tp->md5sig_info->entries4)
+- return NULL;
+- for (i = 0; i < tp->md5sig_info->entries4; i++) {
+- if (tp->md5sig_info->keys4[i].addr == addr)
+- return &tp->md5sig_info->keys4[i].base;
+- }
+- return NULL;
+-}
+-
+-struct tcp_md5sig_key *tcp_v4_md5_lookup(struct sock *sk,
+- struct sock *addr_sk)
+-{
+- return tcp_v4_md5_do_lookup(sk, inet_sk(addr_sk)->daddr);
+-}
+-
+-EXPORT_SYMBOL(tcp_v4_md5_lookup);
+-
+-static struct tcp_md5sig_key *tcp_v4_reqsk_md5_lookup(struct sock *sk,
+- struct request_sock *req)
+-{
+- return tcp_v4_md5_do_lookup(sk, inet_rsk(req)->rmt_addr);
+-}
+-
+-/* This can be called on a newly created socket, from other files */
+-int tcp_v4_md5_do_add(struct sock *sk, __be32 addr,
+- u8 *newkey, u8 newkeylen)
+-{
+- /* Add Key to the list */
+- struct tcp4_md5sig_key *key;
+- struct tcp_sock *tp = tcp_sk(sk);
+- struct tcp4_md5sig_key *keys;
+-
+- key = (struct tcp4_md5sig_key *)tcp_v4_md5_do_lookup(sk, addr);
+- if (key) {
+- /* Pre-existing entry - just update that one. */
+- kfree(key->base.key);
+- key->base.key = newkey;
+- key->base.keylen = newkeylen;
+- } else {
+- struct tcp_md5sig_info *md5sig;
+-
+- if (!tp->md5sig_info) {
+- tp->md5sig_info = kzalloc(sizeof(*tp->md5sig_info),
+- GFP_ATOMIC);
+- if (!tp->md5sig_info) {
+- kfree(newkey);
+- return -ENOMEM;
+- }
+- sk->sk_route_caps &= ~NETIF_F_GSO_MASK;
+- }
+- if (tcp_alloc_md5sig_pool() == NULL) {
+- kfree(newkey);
+- return -ENOMEM;
+- }
+- md5sig = tp->md5sig_info;
+-
+- if (md5sig->alloced4 == md5sig->entries4) {
+- keys = kmalloc((sizeof(*keys) *
+- (md5sig->entries4 + 1)), GFP_ATOMIC);
+- if (!keys) {
+- kfree(newkey);
+- tcp_free_md5sig_pool();
+- return -ENOMEM;
+- }
+-
+- if (md5sig->entries4)
+- memcpy(keys, md5sig->keys4,
+- sizeof(*keys) * md5sig->entries4);
+-
+- /* Free old key list, and reference new one */
+- if (md5sig->keys4)
+- kfree(md5sig->keys4);
+- md5sig->keys4 = keys;
+- md5sig->alloced4++;
+- }
+- md5sig->entries4++;
+- md5sig->keys4[md5sig->entries4 - 1].addr = addr;
+- md5sig->keys4[md5sig->entries4 - 1].base.key = newkey;
+- md5sig->keys4[md5sig->entries4 - 1].base.keylen = newkeylen;
+- }
+- return 0;
+-}
+-
+-EXPORT_SYMBOL(tcp_v4_md5_do_add);
+-
+-static int tcp_v4_md5_add_func(struct sock *sk, struct sock *addr_sk,
+- u8 *newkey, u8 newkeylen)
+-{
+- return tcp_v4_md5_do_add(sk, inet_sk(addr_sk)->daddr,
+- newkey, newkeylen);
+-}
+-
+-int tcp_v4_md5_do_del(struct sock *sk, __be32 addr)
+-{
+- struct tcp_sock *tp = tcp_sk(sk);
+- int i;
+-
+- for (i = 0; i < tp->md5sig_info->entries4; i++) {
+- if (tp->md5sig_info->keys4[i].addr == addr) {
+- /* Free the key */
+- kfree(tp->md5sig_info->keys4[i].base.key);
+- tp->md5sig_info->entries4--;
+-
+- if (tp->md5sig_info->entries4 == 0) {
+- kfree(tp->md5sig_info->keys4);
+- tp->md5sig_info->keys4 = NULL;
+- tp->md5sig_info->alloced4 = 0;
+- } else if (tp->md5sig_info->entries4 != i) {
+- /* Need to do some manipulation */
+- memcpy(&tp->md5sig_info->keys4[i],
+- &tp->md5sig_info->keys4[i+1],
+- (tp->md5sig_info->entries4 - i) *
+- sizeof(struct tcp4_md5sig_key));
+- }
+- tcp_free_md5sig_pool();
+- return 0;
+- }
+- }
+- return -ENOENT;
+-}
+-
+-EXPORT_SYMBOL(tcp_v4_md5_do_del);
+-
+-static void tcp_v4_clear_md5_list(struct sock *sk)
+-{
+- struct tcp_sock *tp = tcp_sk(sk);
+-
+- /* Free each key, then the set of key keys,
+- * the crypto element, and then decrement our
+- * hold on the last resort crypto.
+- */
+- if (tp->md5sig_info->entries4) {
+- int i;
+- for (i = 0; i < tp->md5sig_info->entries4; i++)
+- kfree(tp->md5sig_info->keys4[i].base.key);
+- tp->md5sig_info->entries4 = 0;
+- tcp_free_md5sig_pool();
+- }
+- if (tp->md5sig_info->keys4) {
+- kfree(tp->md5sig_info->keys4);
+- tp->md5sig_info->keys4 = NULL;
+- tp->md5sig_info->alloced4 = 0;
+- }
+-}
+-
+-static int tcp_v4_parse_md5_keys(struct sock *sk, char __user *optval,
+- int optlen)
+-{
+- struct tcp_md5sig cmd;
+- struct sockaddr_in *sin = (struct sockaddr_in *)&cmd.tcpm_addr;
+- u8 *newkey;
+-
+- if (optlen < sizeof(cmd))
+- return -EINVAL;
+-
+- if (copy_from_user(&cmd, optval, sizeof(cmd)))
+- return -EFAULT;
+-
+- if (sin->sin_family != AF_INET)
+- return -EINVAL;
+-
+- if (!cmd.tcpm_key || !cmd.tcpm_keylen) {
+- if (!tcp_sk(sk)->md5sig_info)
+- return -ENOENT;
+- return tcp_v4_md5_do_del(sk, sin->sin_addr.s_addr);
+- }
+-
+- if (cmd.tcpm_keylen > TCP_MD5SIG_MAXKEYLEN)
+- return -EINVAL;
+-
+- if (!tcp_sk(sk)->md5sig_info) {
+- struct tcp_sock *tp = tcp_sk(sk);
+- struct tcp_md5sig_info *p = kzalloc(sizeof(*p), GFP_KERNEL);
+-
+- if (!p)
+- return -EINVAL;
+-
+- tp->md5sig_info = p;
+- sk->sk_route_caps &= ~NETIF_F_GSO_MASK;
+- }
+-
+- newkey = kmemdup(cmd.tcpm_key, cmd.tcpm_keylen, GFP_KERNEL);
+- if (!newkey)
+- return -ENOMEM;
+- return tcp_v4_md5_do_add(sk, sin->sin_addr.s_addr,
+- newkey, cmd.tcpm_keylen);
+-}
+-
+-static int tcp_v4_do_calc_md5_hash(char *md5_hash, struct tcp_md5sig_key *key,
+- __be32 saddr, __be32 daddr,
+- struct tcphdr *th, int protocol,
+- int tcplen)
+-{
+- struct scatterlist sg[4];
+- __u16 data_len;
+- int block = 0;
+- __sum16 old_checksum;
+- struct tcp_md5sig_pool *hp;
+- struct tcp4_pseudohdr *bp;
+- struct hash_desc *desc;
+- int err;
+- unsigned int nbytes = 0;
+-
+- /*
+- * Okay, so RFC2385 is turned on for this connection,
+- * so we need to generate the MD5 hash for the packet now.
+- */
+-
+- hp = tcp_get_md5sig_pool();
+- if (!hp)
+- goto clear_hash_noput;
+-
+- bp = &hp->md5_blk.ip4;
+- desc = &hp->md5_desc;
+-
+- /*
+- * 1. the TCP pseudo-header (in the order: source IP address,
+- * destination IP address, zero-padded protocol number, and
+- * segment length)
+- */
+- bp->saddr = saddr;
+- bp->daddr = daddr;
+- bp->pad = 0;
+- bp->protocol = protocol;
+- bp->len = htons(tcplen);
+- sg_set_buf(&sg[block++], bp, sizeof(*bp));
+- nbytes += sizeof(*bp);
+-
+- /* 2. the TCP header, excluding options, and assuming a
+- * checksum of zero/
+- */
+- old_checksum = th->check;
+- th->check = 0;
+- sg_set_buf(&sg[block++], th, sizeof(struct tcphdr));
+- nbytes += sizeof(struct tcphdr);
+-
+- /* 3. the TCP segment data (if any) */
+- data_len = tcplen - (th->doff << 2);
+- if (data_len > 0) {
+- unsigned char *data = (unsigned char *)th + (th->doff << 2);
+- sg_set_buf(&sg[block++], data, data_len);
+- nbytes += data_len;
+- }
+-
+- /* 4. an independently-specified key or password, known to both
+- * TCPs and presumably connection-specific
+- */
+- sg_set_buf(&sg[block++], key->key, key->keylen);
+- nbytes += key->keylen;
+-
+- /* Now store the Hash into the packet */
+- err = crypto_hash_init(desc);
+- if (err)
+- goto clear_hash;
+- err = crypto_hash_update(desc, sg, nbytes);
+- if (err)
+- goto clear_hash;
+- err = crypto_hash_final(desc, md5_hash);
+- if (err)
+- goto clear_hash;
+-
+- /* Reset header, and free up the crypto */
+- tcp_put_md5sig_pool();
+- th->check = old_checksum;
+-
+-out:
+- return 0;
+-clear_hash:
+- tcp_put_md5sig_pool();
+-clear_hash_noput:
+- memset(md5_hash, 0, 16);
+- goto out;
+-}
+-
+-int tcp_v4_calc_md5_hash(char *md5_hash, struct tcp_md5sig_key *key,
+- struct sock *sk,
+- struct dst_entry *dst,
+- struct request_sock *req,
+- struct tcphdr *th, int protocol,
+- int tcplen)
+-{
+- __be32 saddr, daddr;
+-
+- if (sk) {
+- saddr = inet_sk(sk)->saddr;
+- daddr = inet_sk(sk)->daddr;
+- } else {
+- struct rtable *rt = (struct rtable *)dst;
+- BUG_ON(!rt);
+- saddr = rt->rt_src;
+- daddr = rt->rt_dst;
+- }
+- return tcp_v4_do_calc_md5_hash(md5_hash, key,
+- saddr, daddr,
+- th, protocol, tcplen);
+-}
+-
+-EXPORT_SYMBOL(tcp_v4_calc_md5_hash);
+-
+-static int tcp_v4_inbound_md5_hash(struct sock *sk, struct sk_buff *skb)
+-{
+- /*
+- * This gets called for each TCP segment that arrives
+- * so we want to be efficient.
+- * We have 3 drop cases:
+- * o No MD5 hash and one expected.
+- * o MD5 hash and we're not expecting one.
+- * o MD5 hash and its wrong.
+- */
+- __u8 *hash_location = NULL;
+- struct tcp_md5sig_key *hash_expected;
+- const struct iphdr *iph = ip_hdr(skb);
+- struct tcphdr *th = tcp_hdr(skb);
+- int length = (th->doff << 2) - sizeof(struct tcphdr);
+- int genhash;
+- unsigned char *ptr;
+- unsigned char newhash[16];
+-
+- hash_expected = tcp_v4_md5_do_lookup(sk, iph->saddr);
+-
+- /*
+- * If the TCP option length is less than the TCP_MD5SIG
+- * option length, then we can shortcut
+- */
+- if (length < TCPOLEN_MD5SIG) {
+- if (hash_expected)
+- return 1;
+- else
+- return 0;
+- }
+-
+- /* Okay, we can't shortcut - we have to grub through the options */
+- ptr = (unsigned char *)(th + 1);
+- while (length > 0) {
+- int opcode = *ptr++;
+- int opsize;
+-
+- switch (opcode) {
+- case TCPOPT_EOL:
+- goto done_opts;
+- case TCPOPT_NOP:
+- length--;
+- continue;
+- default:
+- opsize = *ptr++;
+- if (opsize < 2)
+- goto done_opts;
+- if (opsize > length)
+- goto done_opts;
+-
+- if (opcode == TCPOPT_MD5SIG) {
+- hash_location = ptr;
+- goto done_opts;
+- }
+- }
+- ptr += opsize-2;
+- length -= opsize;
+- }
+-done_opts:
+- /* We've parsed the options - do we have a hash? */
+- if (!hash_expected && !hash_location)
+- return 0;
+-
+- if (hash_expected && !hash_location) {
+- LIMIT_NETDEBUG(KERN_INFO "MD5 Hash expected but NOT found "
+- "(" NIPQUAD_FMT ", %d)->(" NIPQUAD_FMT ", %d)\n",
+- NIPQUAD(iph->saddr), ntohs(th->source),
+- NIPQUAD(iph->daddr), ntohs(th->dest));
+- return 1;
+- }
+-
+- if (!hash_expected && hash_location) {
+- LIMIT_NETDEBUG(KERN_INFO "MD5 Hash NOT expected but found "
+- "(" NIPQUAD_FMT ", %d)->(" NIPQUAD_FMT ", %d)\n",
+- NIPQUAD(iph->saddr), ntohs(th->source),
+- NIPQUAD(iph->daddr), ntohs(th->dest));
+- return 1;
+- }
+-
+- /* Okay, so this is hash_expected and hash_location -
+- * so we need to calculate the checksum.
+- */
+- genhash = tcp_v4_do_calc_md5_hash(newhash,
+- hash_expected,
+- iph->saddr, iph->daddr,
+- th, sk->sk_protocol,
+- skb->len);
+-
+- if (genhash || memcmp(hash_location, newhash, 16) != 0) {
+- if (net_ratelimit()) {
+- printk(KERN_INFO "MD5 Hash failed for "
+- "(" NIPQUAD_FMT ", %d)->(" NIPQUAD_FMT ", %d)%s\n",
+- NIPQUAD(iph->saddr), ntohs(th->source),
+- NIPQUAD(iph->daddr), ntohs(th->dest),
+- genhash ? " tcp_v4_calc_md5_hash failed" : "");
+- }
+- return 1;
+- }
+- return 0;
+-}
+-
+-#endif
+-
+-struct request_sock_ops tcp_request_sock_ops __read_mostly = {
+- .family = PF_INET,
+- .obj_size = sizeof(struct tcp_request_sock),
+- .rtx_syn_ack = tcp_v4_send_synack,
+- .send_ack = tcp_v4_reqsk_send_ack,
+- .destructor = tcp_v4_reqsk_destructor,
+- .send_reset = tcp_v4_send_reset,
+-};
+-
+-#ifdef CONFIG_TCP_MD5SIG
+-static struct tcp_request_sock_ops tcp_request_sock_ipv4_ops = {
+- .md5_lookup = tcp_v4_reqsk_md5_lookup,
+-};
+-#endif
+-
+-static struct timewait_sock_ops tcp_timewait_sock_ops = {
+- .twsk_obj_size = sizeof(struct tcp_timewait_sock),
+- .twsk_unique = tcp_twsk_unique,
+- .twsk_destructor= tcp_twsk_destructor,
+-};
+-
+-int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
+-{
+- struct inet_request_sock *ireq;
+- struct tcp_options_received tmp_opt;
+- struct request_sock *req;
+- __be32 saddr = ip_hdr(skb)->saddr;
+- __be32 daddr = ip_hdr(skb)->daddr;
+- __u32 isn = TCP_SKB_CB(skb)->when;
+- struct dst_entry *dst = NULL;
+-#ifdef CONFIG_SYN_COOKIES
+- int want_cookie = 0;
+-#else
+-#define want_cookie 0 /* Argh, why doesn't gcc optimize this :( */
+-#endif
+-
+- /* Never answer to SYNs send to broadcast or multicast */
+- if (((struct rtable *)skb->dst)->rt_flags &
+- (RTCF_BROADCAST | RTCF_MULTICAST))
+- goto drop;
+-
+- /* TW buckets are converted to open requests without
+- * limitations, they conserve resources and peer is
+- * evidently real one.
+- */
+- if (inet_csk_reqsk_queue_is_full(sk) && !isn) {
+-#ifdef CONFIG_SYN_COOKIES
+- if (sysctl_tcp_syncookies) {
+- want_cookie = 1;
+- } else
+-#endif
+- goto drop;
+- }
+-
+- /* Accept backlog is full. If we have already queued enough
+- * of warm entries in syn queue, drop request. It is better than
+- * clogging syn queue with openreqs with exponentially increasing
+- * timeout.
+- */
+- if (sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_young(sk) > 1)
+- goto drop;
+-
+- req = reqsk_alloc(&tcp_request_sock_ops);
+- if (!req)
+- goto drop;
+-
+-#ifdef CONFIG_TCP_MD5SIG
+- tcp_rsk(req)->af_specific = &tcp_request_sock_ipv4_ops;
+-#endif
+-
+- tcp_clear_options(&tmp_opt);
+- tmp_opt.mss_clamp = 536;
+- tmp_opt.user_mss = tcp_sk(sk)->rx_opt.user_mss;
+-
+- tcp_parse_options(skb, &tmp_opt, 0);
+-
+- if (want_cookie) {
+- tcp_clear_options(&tmp_opt);
+- tmp_opt.saw_tstamp = 0;
+- }
+-
+- if (tmp_opt.saw_tstamp && !tmp_opt.rcv_tsval) {
+- /* Some OSes (unknown ones, but I see them on web server, which
+- * contains information interesting only for windows'
+- * users) do not send their stamp in SYN. It is easy case.
+- * We simply do not advertise TS support.
+- */
+- tmp_opt.saw_tstamp = 0;
+- tmp_opt.tstamp_ok = 0;
+- }
+- tmp_opt.tstamp_ok = tmp_opt.saw_tstamp;
+-
+- tcp_openreq_init(req, &tmp_opt, skb);
+-
+- if (security_inet_conn_request(sk, skb, req))
+- goto drop_and_free;
+-
+- ireq = inet_rsk(req);
+- ireq->loc_addr = daddr;
+- ireq->rmt_addr = saddr;
+- ireq->opt = tcp_v4_save_options(sk, skb);
+- if (!want_cookie)
+- TCP_ECN_create_request(req, tcp_hdr(skb));
+-
+- if (want_cookie) {
+-#ifdef CONFIG_SYN_COOKIES
+- syn_flood_warning(skb);
+-#endif
+- isn = cookie_v4_init_sequence(sk, skb, &req->mss);
+- } else if (!isn) {
+- struct inet_peer *peer = NULL;
+-
+- /* VJ's idea. We save last timestamp seen
+- * from the destination in peer table, when entering
+- * state TIME-WAIT, and check against it before
+- * accepting new connection request.
+- *
+- * If "isn" is not zero, this request hit alive
+- * timewait bucket, so that all the necessary checks
+- * are made in the function processing timewait state.
+- */
+- if (tmp_opt.saw_tstamp &&
+- tcp_death_row.sysctl_tw_recycle &&
+- (dst = inet_csk_route_req(sk, req)) != NULL &&
+- (peer = rt_get_peer((struct rtable *)dst)) != NULL &&
+- peer->v4daddr == saddr) {
+- if (get_seconds() < peer->tcp_ts_stamp + TCP_PAWS_MSL &&
+- (s32)(peer->tcp_ts - req->ts_recent) >
+- TCP_PAWS_WINDOW) {
+- NET_INC_STATS_BH(LINUX_MIB_PAWSPASSIVEREJECTED);
+- dst_release(dst);
+- goto drop_and_free;
+- }
+- }
+- /* Kill the following clause, if you dislike this way. */
+- else if (!sysctl_tcp_syncookies &&
+- (sysctl_max_syn_backlog - inet_csk_reqsk_queue_len(sk) <
+- (sysctl_max_syn_backlog >> 2)) &&
+- (!peer || !peer->tcp_ts_stamp) &&
+- (!dst || !dst_metric(dst, RTAX_RTT))) {
+- /* Without syncookies last quarter of
+- * backlog is filled with destinations,
+- * proven to be alive.
+- * It means that we continue to communicate
+- * to destinations, already remembered
+- * to the moment of synflood.
+- */
+- LIMIT_NETDEBUG(KERN_DEBUG "TCP: drop open "
+- "request from %u.%u.%u.%u/%u\n",
+- NIPQUAD(saddr),
+- ntohs(tcp_hdr(skb)->source));
+- dst_release(dst);
+- goto drop_and_free;
+- }
+-
+- isn = tcp_v4_init_sequence(skb);
+- }
+- tcp_rsk(req)->snt_isn = isn;
+-
+- if (tcp_v4_send_synack(sk, req, dst))
+- goto drop_and_free;
+-
+- if (want_cookie) {
+- reqsk_free(req);
+- } else {
+- inet_csk_reqsk_queue_hash_add(sk, req, TCP_TIMEOUT_INIT);
+- }
+- return 0;
+-
+-drop_and_free:
+- reqsk_free(req);
+-drop:
+- return 0;
+-}
+-
+-
+-/*
+- * The three way handshake has completed - we got a valid synack -
+- * now create the new socket.
+- */
+-struct sock *tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
+- struct request_sock *req,
+- struct dst_entry *dst)
+-{
+- struct inet_request_sock *ireq;
+- struct inet_sock *newinet;
+- struct tcp_sock *newtp;
+- struct sock *newsk;
+-#ifdef CONFIG_TCP_MD5SIG
+- struct tcp_md5sig_key *key;
+-#endif
+-
+- if (sk_acceptq_is_full(sk))
+- goto exit_overflow;
+-
+- if (!dst && (dst = inet_csk_route_req(sk, req)) == NULL)
+- goto exit;
+-
+- newsk = tcp_create_openreq_child(sk, req, skb);
+- if (!newsk)
+- goto exit;
+-
+- newsk->sk_gso_type = SKB_GSO_TCPV4;
+- sk_setup_caps(newsk, dst);
+-
+- newtp = tcp_sk(newsk);
+- newinet = inet_sk(newsk);
+- ireq = inet_rsk(req);
+- newinet->daddr = ireq->rmt_addr;
+- newinet->rcv_saddr = ireq->loc_addr;
+- newinet->saddr = ireq->loc_addr;
+- newinet->opt = ireq->opt;
+- ireq->opt = NULL;
+- newinet->mc_index = inet_iif(skb);
+- newinet->mc_ttl = ip_hdr(skb)->ttl;
+- inet_csk(newsk)->icsk_ext_hdr_len = 0;
+- if (newinet->opt)
+- inet_csk(newsk)->icsk_ext_hdr_len = newinet->opt->optlen;
+- newinet->id = newtp->write_seq ^ jiffies;
+-
+- tcp_mtup_init(newsk);
+- tcp_sync_mss(newsk, dst_mtu(dst));
+- newtp->advmss = dst_metric(dst, RTAX_ADVMSS);
+- tcp_initialize_rcv_mss(newsk);
+-
+-#ifdef CONFIG_TCP_MD5SIG
+- /* Copy over the MD5 key from the original socket */
+- if ((key = tcp_v4_md5_do_lookup(sk, newinet->daddr)) != NULL) {
+- /*
+- * We're using one, so create a matching key
+- * on the newsk structure. If we fail to get
+- * memory, then we end up not copying the key
+- * across. Shucks.
+- */
+- char *newkey = kmemdup(key->key, key->keylen, GFP_ATOMIC);
+- if (newkey != NULL)
+- tcp_v4_md5_do_add(newsk, inet_sk(sk)->daddr,
+- newkey, key->keylen);
+- }
+-#endif
+-
+- __inet_hash(&tcp_hashinfo, newsk, 0);
+- __inet_inherit_port(&tcp_hashinfo, sk, newsk);
+-
+- return newsk;
+-
+-exit_overflow:
+- NET_INC_STATS_BH(LINUX_MIB_LISTENOVERFLOWS);
+-exit:
+- NET_INC_STATS_BH(LINUX_MIB_LISTENDROPS);
+- dst_release(dst);
+- return NULL;
+-}
+-
+-static struct sock *tcp_v4_hnd_req(struct sock *sk, struct sk_buff *skb)
+-{
+- struct tcphdr *th = tcp_hdr(skb);
+- const struct iphdr *iph = ip_hdr(skb);
+- struct sock *nsk;
+- struct request_sock **prev;
+- /* Find possible connection requests. */
+- struct request_sock *req = inet_csk_search_req(sk, &prev, th->source,
+- iph->saddr, iph->daddr);
+- if (req)
+- return tcp_check_req(sk, skb, req, prev);
+-
+- nsk = inet_lookup_established(&tcp_hashinfo, iph->saddr, th->source,
+- iph->daddr, th->dest, inet_iif(skb));
+-
+- if (nsk) {
+- if (nsk->sk_state != TCP_TIME_WAIT) {
+- bh_lock_sock(nsk);
+- return nsk;
+- }
+- inet_twsk_put(inet_twsk(nsk));
+- return NULL;
+- }
+-
+-#ifdef CONFIG_SYN_COOKIES
+- if (!th->rst && !th->syn && th->ack)
+- sk = cookie_v4_check(sk, skb, &(IPCB(skb)->opt));
+-#endif
+- return sk;
+-}
+-
+-static __sum16 tcp_v4_checksum_init(struct sk_buff *skb)
+-{
+- const struct iphdr *iph = ip_hdr(skb);
+-
+- if (skb->ip_summed == CHECKSUM_COMPLETE) {
+- if (!tcp_v4_check(skb->len, iph->saddr,
+- iph->daddr, skb->csum)) {
+- skb->ip_summed = CHECKSUM_UNNECESSARY;
+- return 0;
+- }
+- }
+-
+- skb->csum = csum_tcpudp_nofold(iph->saddr, iph->daddr,
+- skb->len, IPPROTO_TCP, 0);
+-
+- if (skb->len <= 76) {
+- return __skb_checksum_complete(skb);
+- }
+- return 0;
+-}
+-
+-
+-/* The socket must have it's spinlock held when we get
+- * here.
+- *
+- * We have a potential double-lock case here, so even when
+- * doing backlog processing we use the BH locking scheme.
+- * This is because we cannot sleep with the original spinlock
+- * held.
+- */
+-int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb)
+-{
+- struct sock *rsk;
+-#ifdef CONFIG_TCP_MD5SIG
+- /*
+- * We really want to reject the packet as early as possible
+- * if:
+- * o We're expecting an MD5'd packet and this is no MD5 tcp option
+- * o There is an MD5 option and we're not expecting one
+- */
+- if (tcp_v4_inbound_md5_hash(sk, skb))
+- goto discard;
+-#endif
+-
+- if (sk->sk_state == TCP_ESTABLISHED) { /* Fast path */
+- TCP_CHECK_TIMER(sk);
+- if (tcp_rcv_established(sk, skb, tcp_hdr(skb), skb->len)) {
+- rsk = sk;
+- goto reset;
+- }
+- TCP_CHECK_TIMER(sk);
+- return 0;
+- }
+-
+- if (skb->len < tcp_hdrlen(skb) || tcp_checksum_complete(skb))
+- goto csum_err;
+-
+- if (sk->sk_state == TCP_LISTEN) {
+- struct sock *nsk = tcp_v4_hnd_req(sk, skb);
+- if (!nsk)
+- goto discard;
+-
+- if (nsk != sk) {
+- if (tcp_child_process(sk, nsk, skb)) {
+- rsk = nsk;
+- goto reset;
+- }
+- return 0;
+- }
+- }
+-
+- TCP_CHECK_TIMER(sk);
+- if (tcp_rcv_state_process(sk, skb, tcp_hdr(skb), skb->len)) {
+- rsk = sk;
+- goto reset;
+- }
+- TCP_CHECK_TIMER(sk);
+- return 0;
+-
+-reset:
+- tcp_v4_send_reset(rsk, skb);
+-discard:
+- kfree_skb(skb);
+- /* Be careful here. If this function gets more complicated and
+- * gcc suffers from register pressure on the x86, sk (in %ebx)
+- * might be destroyed here. This current version compiles correctly,
+- * but you have been warned.
+- */
+- return 0;
+-
+-csum_err:
+- TCP_INC_STATS_BH(TCP_MIB_INERRS);
+- goto discard;
+-}
+-
+-/*
+- * From tcp_input.c
+- */
+-
+-int tcp_v4_rcv(struct sk_buff *skb)
+-{
+- const struct iphdr *iph;
+- struct tcphdr *th;
+- struct sock *sk;
+- int ret;
+-
+- if (skb->pkt_type != PACKET_HOST)
+- goto discard_it;
+-
+- /* Count it even if it's bad */
+- TCP_INC_STATS_BH(TCP_MIB_INSEGS);
+-
+- if (!pskb_may_pull(skb, sizeof(struct tcphdr)))
+- goto discard_it;
+-
+- th = tcp_hdr(skb);
+-
+- if (th->doff < sizeof(struct tcphdr) / 4)
+- goto bad_packet;
+- if (!pskb_may_pull(skb, th->doff * 4))
+- goto discard_it;
+-
+- /* An explanation is required here, I think.
+- * Packet length and doff are validated by header prediction,
+- * provided case of th->doff==0 is eliminated.
+- * So, we defer the checks. */
+- if (!skb_csum_unnecessary(skb) && tcp_v4_checksum_init(skb))
+- goto bad_packet;
+-
+- th = tcp_hdr(skb);
+- iph = ip_hdr(skb);
+- TCP_SKB_CB(skb)->seq = ntohl(th->seq);
+- TCP_SKB_CB(skb)->end_seq = (TCP_SKB_CB(skb)->seq + th->syn + th->fin +
+- skb->len - th->doff * 4);
+- TCP_SKB_CB(skb)->ack_seq = ntohl(th->ack_seq);
+- TCP_SKB_CB(skb)->when = 0;
+- TCP_SKB_CB(skb)->flags = iph->tos;
+- TCP_SKB_CB(skb)->sacked = 0;
+-
+- sk = __inet_lookup(&tcp_hashinfo, iph->saddr, th->source,
+- iph->daddr, th->dest, inet_iif(skb));
+- if (!sk)
+- goto no_tcp_socket;
+-
+-process:
+- if (sk->sk_state == TCP_TIME_WAIT)
+- goto do_time_wait;
+-
+- if (!xfrm4_policy_check(sk, XFRM_POLICY_IN, skb))
+- goto discard_and_relse;
+- nf_reset(skb);
+-
+- if (sk_filter(sk, skb))
+- goto discard_and_relse;
+-
+- skb->dev = NULL;
+-
+- bh_lock_sock_nested(sk);
+- ret = 0;
+- if (!sock_owned_by_user(sk)) {
+-#ifdef CONFIG_NET_DMA
+- struct tcp_sock *tp = tcp_sk(sk);
+- if (!tp->ucopy.dma_chan && tp->ucopy.pinned_list)
+- tp->ucopy.dma_chan = get_softnet_dma();
+- if (tp->ucopy.dma_chan)
+- ret = tcp_v4_do_rcv(sk, skb);
+- else
+-#endif
+- {
+- if (!tcp_prequeue(sk, skb))
+- ret = tcp_v4_do_rcv(sk, skb);
+- }
+- } else
+- sk_add_backlog(sk, skb);
+- bh_unlock_sock(sk);
+-
+- sock_put(sk);
+-
+- return ret;
+-
+-no_tcp_socket:
+- if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb))
+- goto discard_it;
+-
+- if (skb->len < (th->doff << 2) || tcp_checksum_complete(skb)) {
+-bad_packet:
+- TCP_INC_STATS_BH(TCP_MIB_INERRS);
+- } else {
+- tcp_v4_send_reset(NULL, skb);
+- }
+-
+-discard_it:
+- /* Discard frame. */
+- kfree_skb(skb);
+- return 0;
+-
+-discard_and_relse:
+- sock_put(sk);
+- goto discard_it;
+-
+-do_time_wait:
+- if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) {
+- inet_twsk_put(inet_twsk(sk));
+- goto discard_it;
+- }
+-
+- if (skb->len < (th->doff << 2) || tcp_checksum_complete(skb)) {
+- TCP_INC_STATS_BH(TCP_MIB_INERRS);
+- inet_twsk_put(inet_twsk(sk));
+- goto discard_it;
+- }
+- switch (tcp_timewait_state_process(inet_twsk(sk), skb, th)) {
+- case TCP_TW_SYN: {
+- struct sock *sk2 = inet_lookup_listener(&tcp_hashinfo,
+- iph->daddr, th->dest,
+- inet_iif(skb));
+- if (sk2) {
+- inet_twsk_deschedule(inet_twsk(sk), &tcp_death_row);
+- inet_twsk_put(inet_twsk(sk));
+- sk = sk2;
+- goto process;
+- }
+- /* Fall through to ACK */
+- }
+- case TCP_TW_ACK:
+- tcp_v4_timewait_ack(sk, skb);
+- break;
+- case TCP_TW_RST:
+- goto no_tcp_socket;
+- case TCP_TW_SUCCESS:;
+- }
+- goto discard_it;
+-}
+-
+-/* VJ's idea. Save last timestamp seen from this destination
+- * and hold it at least for normal timewait interval to use for duplicate
+- * segment detection in subsequent connections, before they enter synchronized
+- * state.
+- */
+-
+-int tcp_v4_remember_stamp(struct sock *sk)
+-{
+- struct inet_sock *inet = inet_sk(sk);
+- struct tcp_sock *tp = tcp_sk(sk);
+- struct rtable *rt = (struct rtable *)__sk_dst_get(sk);
+- struct inet_peer *peer = NULL;
+- int release_it = 0;
+-
+- if (!rt || rt->rt_dst != inet->daddr) {
+- peer = inet_getpeer(inet->daddr, 1);
+- release_it = 1;
+- } else {
+- if (!rt->peer)
+- rt_bind_peer(rt, 1);
+- peer = rt->peer;
+- }
+-
+- if (peer) {
+- if ((s32)(peer->tcp_ts - tp->rx_opt.ts_recent) <= 0 ||
+- (peer->tcp_ts_stamp + TCP_PAWS_MSL < get_seconds() &&
+- peer->tcp_ts_stamp <= tp->rx_opt.ts_recent_stamp)) {
+- peer->tcp_ts_stamp = tp->rx_opt.ts_recent_stamp;
+- peer->tcp_ts = tp->rx_opt.ts_recent;
+- }
+- if (release_it)
+- inet_putpeer(peer);
+- return 1;
+- }
+-
+- return 0;
+-}
+-
+-int tcp_v4_tw_remember_stamp(struct inet_timewait_sock *tw)
+-{
+- struct inet_peer *peer = inet_getpeer(tw->tw_daddr, 1);
+-
+- if (peer) {
+- const struct tcp_timewait_sock *tcptw = tcp_twsk((struct sock *)tw);
+-
+- if ((s32)(peer->tcp_ts - tcptw->tw_ts_recent) <= 0 ||
+- (peer->tcp_ts_stamp + TCP_PAWS_MSL < get_seconds() &&
+- peer->tcp_ts_stamp <= tcptw->tw_ts_recent_stamp)) {
+- peer->tcp_ts_stamp = tcptw->tw_ts_recent_stamp;
+- peer->tcp_ts = tcptw->tw_ts_recent;
+- }
+- inet_putpeer(peer);
+- return 1;
+- }
+-
+- return 0;
+-}
+-
+-struct inet_connection_sock_af_ops ipv4_specific = {
+- .queue_xmit = ip_queue_xmit,
+- .send_check = tcp_v4_send_check,
+- .rebuild_header = inet_sk_rebuild_header,
+- .conn_request = tcp_v4_conn_request,
+- .syn_recv_sock = tcp_v4_syn_recv_sock,
+- .remember_stamp = tcp_v4_remember_stamp,
+- .net_header_len = sizeof(struct iphdr),
+- .setsockopt = ip_setsockopt,
+- .getsockopt = ip_getsockopt,
+- .addr2sockaddr = inet_csk_addr2sockaddr,
+- .sockaddr_len = sizeof(struct sockaddr_in),
+-#ifdef CONFIG_COMPAT
+- .compat_setsockopt = compat_ip_setsockopt,
+- .compat_getsockopt = compat_ip_getsockopt,
+-#endif
+-};
+-
+-#ifdef CONFIG_TCP_MD5SIG
+-static struct tcp_sock_af_ops tcp_sock_ipv4_specific = {
+- .md5_lookup = tcp_v4_md5_lookup,
+- .calc_md5_hash = tcp_v4_calc_md5_hash,
+- .md5_add = tcp_v4_md5_add_func,
+- .md5_parse = tcp_v4_parse_md5_keys,
+-};
+-#endif
+-
+-/* NOTE: A lot of things set to zero explicitly by call to
+- * sk_alloc() so need not be done here.
+- */
+-static int tcp_v4_init_sock(struct sock *sk)
+-{
+- struct inet_connection_sock *icsk = inet_csk(sk);
+- struct tcp_sock *tp = tcp_sk(sk);
+-
+- skb_queue_head_init(&tp->out_of_order_queue);
+- tcp_init_xmit_timers(sk);
+- tcp_prequeue_init(tp);
+-
+- icsk->icsk_rto = TCP_TIMEOUT_INIT;
+- tp->mdev = TCP_TIMEOUT_INIT;
+-
+- /* So many TCP implementations out there (incorrectly) count the
+- * initial SYN frame in their delayed-ACK and congestion control
+- * algorithms that we must have the following bandaid to talk
+- * efficiently to them. -DaveM
+- */
+- tp->snd_cwnd = 2;
+-
+- /* See draft-stevens-tcpca-spec-01 for discussion of the
+- * initialization of these values.
+- */
+- tp->snd_ssthresh = 0x7fffffff; /* Infinity */
+- tp->snd_cwnd_clamp = ~0;
+- tp->mss_cache = 536;
+-
+- tp->reordering = sysctl_tcp_reordering;
+- icsk->icsk_ca_ops = &tcp_init_congestion_ops;
+-
+- sk->sk_state = TCP_CLOSE;
+-
+- sk->sk_write_space = sk_stream_write_space;
+- sock_set_flag(sk, SOCK_USE_WRITE_QUEUE);
+-
+- icsk->icsk_af_ops = &ipv4_specific;
+- icsk->icsk_sync_mss = tcp_sync_mss;
+-#ifdef CONFIG_TCP_MD5SIG
+- tp->af_specific = &tcp_sock_ipv4_specific;
+-#endif
+-
+- sk->sk_sndbuf = sysctl_tcp_wmem[1];
+- sk->sk_rcvbuf = sysctl_tcp_rmem[1];
+-
+- atomic_inc(&tcp_sockets_allocated);
+-
+- return 0;
+-}
+-
+-int tcp_v4_destroy_sock(struct sock *sk)
+-{
+- struct tcp_sock *tp = tcp_sk(sk);
+-
+- tcp_clear_xmit_timers(sk);
+-
+- tcp_cleanup_congestion_control(sk);
+-
+- /* Cleanup up the write buffer. */
+- tcp_write_queue_purge(sk);
+-
+- /* Cleans up our, hopefully empty, out_of_order_queue. */
+- __skb_queue_purge(&tp->out_of_order_queue);
+-
+-#ifdef CONFIG_TCP_MD5SIG
+- /* Clean up the MD5 key list, if any */
+- if (tp->md5sig_info) {
+- tcp_v4_clear_md5_list(sk);
+- kfree(tp->md5sig_info);
+- tp->md5sig_info = NULL;
+- }
+-#endif
+-
+-#ifdef CONFIG_NET_DMA
+- /* Cleans up our sk_async_wait_queue */
+- __skb_queue_purge(&sk->sk_async_wait_queue);
+-#endif
+-
+- /* Clean prequeue, it must be empty really */
+- __skb_queue_purge(&tp->ucopy.prequeue);
+-
+- /* Clean up a referenced TCP bind bucket. */
+- if (inet_csk(sk)->icsk_bind_hash)
+- inet_put_port(&tcp_hashinfo, sk);
+-
+- /*
+- * If sendmsg cached page exists, toss it.
+- */
+- if (sk->sk_sndmsg_page) {
+- __free_page(sk->sk_sndmsg_page);
+- sk->sk_sndmsg_page = NULL;
+- }
+-
+- atomic_dec(&tcp_sockets_allocated);
+-
+- return 0;
+-}
+-
+-EXPORT_SYMBOL(tcp_v4_destroy_sock);
+-
+-#ifdef CONFIG_PROC_FS
+-/* Proc filesystem TCP sock list dumping. */
+-
+-static inline struct inet_timewait_sock *tw_head(struct hlist_head *head)
+-{
+- return hlist_empty(head) ? NULL :
+- list_entry(head->first, struct inet_timewait_sock, tw_node);
+-}
+-
+-static inline struct inet_timewait_sock *tw_next(struct inet_timewait_sock *tw)
+-{
+- return tw->tw_node.next ?
+- hlist_entry(tw->tw_node.next, typeof(*tw), tw_node) : NULL;
+-}
+-
+-static void *listening_get_next(struct seq_file *seq, void *cur)
+-{
+- struct inet_connection_sock *icsk;
+- struct hlist_node *node;
+- struct sock *sk = cur;
+- struct tcp_iter_state* st = seq->private;
+-
+- if (!sk) {
+- st->bucket = 0;
+- sk = sk_head(&tcp_hashinfo.listening_hash[0]);
+- goto get_sk;
+- }
+-
+- ++st->num;
+-
+- if (st->state == TCP_SEQ_STATE_OPENREQ) {
+- struct request_sock *req = cur;
+-
+- icsk = inet_csk(st->syn_wait_sk);
+- req = req->dl_next;
+- while (1) {
+- while (req) {
+- if (req->rsk_ops->family == st->family) {
+- cur = req;
+- goto out;
+- }
+- req = req->dl_next;
+- }
+- if (++st->sbucket >= icsk->icsk_accept_queue.listen_opt->nr_table_entries)
+- break;
+-get_req:
+- req = icsk->icsk_accept_queue.listen_opt->syn_table[st->sbucket];
+- }
+- sk = sk_next(st->syn_wait_sk);
+- st->state = TCP_SEQ_STATE_LISTENING;
+- read_unlock_bh(&icsk->icsk_accept_queue.syn_wait_lock);
+- } else {
+- icsk = inet_csk(sk);
+- read_lock_bh(&icsk->icsk_accept_queue.syn_wait_lock);
+- if (reqsk_queue_len(&icsk->icsk_accept_queue))
+- goto start_req;
+- read_unlock_bh(&icsk->icsk_accept_queue.syn_wait_lock);
+- sk = sk_next(sk);
+- }
+-get_sk:
+- sk_for_each_from(sk, node) {
+- if (sk->sk_family == st->family) {
+- cur = sk;
+- goto out;
+- }
+- icsk = inet_csk(sk);
+- read_lock_bh(&icsk->icsk_accept_queue.syn_wait_lock);
+- if (reqsk_queue_len(&icsk->icsk_accept_queue)) {
+-start_req:
+- st->uid = sock_i_uid(sk);
+- st->syn_wait_sk = sk;
+- st->state = TCP_SEQ_STATE_OPENREQ;
+- st->sbucket = 0;
+- goto get_req;
+- }
+- read_unlock_bh(&icsk->icsk_accept_queue.syn_wait_lock);
+- }
+- if (++st->bucket < INET_LHTABLE_SIZE) {
+- sk = sk_head(&tcp_hashinfo.listening_hash[st->bucket]);
+- goto get_sk;
+- }
+- cur = NULL;
+-out:
+- return cur;
+-}
+-
+-static void *listening_get_idx(struct seq_file *seq, loff_t *pos)
+-{
+- void *rc = listening_get_next(seq, NULL);
+-
+- while (rc && *pos) {
+- rc = listening_get_next(seq, rc);
+- --*pos;
+- }
+- return rc;
+-}
+-
+-static void *established_get_first(struct seq_file *seq)
+-{
+- struct tcp_iter_state* st = seq->private;
+- void *rc = NULL;
+-
+- for (st->bucket = 0; st->bucket < tcp_hashinfo.ehash_size; ++st->bucket) {
+- struct sock *sk;
+- struct hlist_node *node;
+- struct inet_timewait_sock *tw;
+-
+- /* We can reschedule _before_ having picked the target: */
+- cond_resched_softirq();
+-
+- read_lock(&tcp_hashinfo.ehash[st->bucket].lock);
+- sk_for_each(sk, node, &tcp_hashinfo.ehash[st->bucket].chain) {
+- if (sk->sk_family != st->family) {
+- continue;
+- }
+- rc = sk;
+- goto out;
+- }
+- st->state = TCP_SEQ_STATE_TIME_WAIT;
+- inet_twsk_for_each(tw, node,
+- &tcp_hashinfo.ehash[st->bucket].twchain) {
+- if (tw->tw_family != st->family) {
+- continue;
+- }
+- rc = tw;
+- goto out;
+- }
+- read_unlock(&tcp_hashinfo.ehash[st->bucket].lock);
+- st->state = TCP_SEQ_STATE_ESTABLISHED;
+- }
+-out:
+- return rc;
+-}
+-
+-static void *established_get_next(struct seq_file *seq, void *cur)
+-{
+- struct sock *sk = cur;
+- struct inet_timewait_sock *tw;
+- struct hlist_node *node;
+- struct tcp_iter_state* st = seq->private;
+-
+- ++st->num;
+-
+- if (st->state == TCP_SEQ_STATE_TIME_WAIT) {
+- tw = cur;
+- tw = tw_next(tw);
+-get_tw:
+- while (tw && tw->tw_family != st->family) {
+- tw = tw_next(tw);
+- }
+- if (tw) {
+- cur = tw;
+- goto out;
+- }
+- read_unlock(&tcp_hashinfo.ehash[st->bucket].lock);
+- st->state = TCP_SEQ_STATE_ESTABLISHED;
+-
+- /* We can reschedule between buckets: */
+- cond_resched_softirq();
+-
+- if (++st->bucket < tcp_hashinfo.ehash_size) {
+- read_lock(&tcp_hashinfo.ehash[st->bucket].lock);
+- sk = sk_head(&tcp_hashinfo.ehash[st->bucket].chain);
+- } else {
+- cur = NULL;
+- goto out;
+- }
+- } else
+- sk = sk_next(sk);
+-
+- sk_for_each_from(sk, node) {
+- if (sk->sk_family == st->family)
+- goto found;
+- }
+-
+- st->state = TCP_SEQ_STATE_TIME_WAIT;
+- tw = tw_head(&tcp_hashinfo.ehash[st->bucket].twchain);
+- goto get_tw;
+-found:
+- cur = sk;
+-out:
+- return cur;
+-}
+-
+-static void *established_get_idx(struct seq_file *seq, loff_t pos)
+-{
+- void *rc = established_get_first(seq);
+-
+- while (rc && pos) {
+- rc = established_get_next(seq, rc);
+- --pos;
+- }
+- return rc;
+-}
+-
+-static void *tcp_get_idx(struct seq_file *seq, loff_t pos)
+-{
+- void *rc;
+- struct tcp_iter_state* st = seq->private;
+-
+- inet_listen_lock(&tcp_hashinfo);
+- st->state = TCP_SEQ_STATE_LISTENING;
+- rc = listening_get_idx(seq, &pos);
+-
+- if (!rc) {
+- inet_listen_unlock(&tcp_hashinfo);
+- local_bh_disable();
+- st->state = TCP_SEQ_STATE_ESTABLISHED;
+- rc = established_get_idx(seq, pos);
+- }
+-
+- return rc;
+-}
+-
+-static void *tcp_seq_start(struct seq_file *seq, loff_t *pos)
+-{
+- struct tcp_iter_state* st = seq->private;
+- st->state = TCP_SEQ_STATE_LISTENING;
+- st->num = 0;
+- return *pos ? tcp_get_idx(seq, *pos - 1) : SEQ_START_TOKEN;
+-}
+-
+-static void *tcp_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+-{
+- void *rc = NULL;
+- struct tcp_iter_state* st;
+-
+- if (v == SEQ_START_TOKEN) {
+- rc = tcp_get_idx(seq, 0);
+- goto out;
+- }
+- st = seq->private;
+-
+- switch (st->state) {
+- case TCP_SEQ_STATE_OPENREQ:
+- case TCP_SEQ_STATE_LISTENING:
+- rc = listening_get_next(seq, v);
+- if (!rc) {
+- inet_listen_unlock(&tcp_hashinfo);
+- local_bh_disable();
+- st->state = TCP_SEQ_STATE_ESTABLISHED;
+- rc = established_get_first(seq);
+- }
+- break;
+- case TCP_SEQ_STATE_ESTABLISHED:
+- case TCP_SEQ_STATE_TIME_WAIT:
+- rc = established_get_next(seq, v);
+- break;
+- }
+-out:
+- ++*pos;
+- return rc;
+-}
+-
+-static void tcp_seq_stop(struct seq_file *seq, void *v)
+-{
+- struct tcp_iter_state* st = seq->private;
+-
+- switch (st->state) {
+- case TCP_SEQ_STATE_OPENREQ:
+- if (v) {
+- struct inet_connection_sock *icsk = inet_csk(st->syn_wait_sk);
+- read_unlock_bh(&icsk->icsk_accept_queue.syn_wait_lock);
+- }
+- case TCP_SEQ_STATE_LISTENING:
+- if (v != SEQ_START_TOKEN)
+- inet_listen_unlock(&tcp_hashinfo);
+- break;
+- case TCP_SEQ_STATE_TIME_WAIT:
+- case TCP_SEQ_STATE_ESTABLISHED:
+- if (v)
+- read_unlock(&tcp_hashinfo.ehash[st->bucket].lock);
+- local_bh_enable();
+- break;
+- }
+-}
+-
+-static int tcp_seq_open(struct inode *inode, struct file *file)
+-{
+- struct tcp_seq_afinfo *afinfo = PDE(inode)->data;
+- struct seq_file *seq;
+- struct tcp_iter_state *s;
+- int rc;
+-
+- if (unlikely(afinfo == NULL))
+- return -EINVAL;
+-
+- s = kzalloc(sizeof(*s), GFP_KERNEL);
+- if (!s)
+- return -ENOMEM;
+- s->family = afinfo->family;
+- s->seq_ops.start = tcp_seq_start;
+- s->seq_ops.next = tcp_seq_next;
+- s->seq_ops.show = afinfo->seq_show;
+- s->seq_ops.stop = tcp_seq_stop;
+-
+- rc = seq_open(file, &s->seq_ops);
+- if (rc)
+- goto out_kfree;
+- seq = file->private_data;
+- seq->private = s;
+-out:
+- return rc;
+-out_kfree:
+- kfree(s);
+- goto out;
+-}
+-
+-int tcp_proc_register(struct tcp_seq_afinfo *afinfo)
+-{
+- int rc = 0;
+- struct proc_dir_entry *p;
+-
+- if (!afinfo)
+- return -EINVAL;
+- afinfo->seq_fops->owner = afinfo->owner;
+- afinfo->seq_fops->open = tcp_seq_open;
+- afinfo->seq_fops->read = seq_read;
+- afinfo->seq_fops->llseek = seq_lseek;
+- afinfo->seq_fops->release = seq_release_private;
+-
+- p = proc_net_fops_create(afinfo->name, S_IRUGO, afinfo->seq_fops);
+- if (p)
+- p->data = afinfo;
+- else
+- rc = -ENOMEM;
+- return rc;
+-}
+-
+-void tcp_proc_unregister(struct tcp_seq_afinfo *afinfo)
+-{
+- if (!afinfo)
+- return;
+- proc_net_remove(afinfo->name);
+- memset(afinfo->seq_fops, 0, sizeof(*afinfo->seq_fops));
+-}
+-
+-static void get_openreq4(struct sock *sk, struct request_sock *req,
+- char *tmpbuf, int i, int uid)
+-{
+- const struct inet_request_sock *ireq = inet_rsk(req);
+- int ttd = req->expires - jiffies;
+-
+- sprintf(tmpbuf, "%4d: %08X:%04X %08X:%04X"
+- " %02X %08X:%08X %02X:%08lX %08X %5d %8d %u %d %p",
+- i,
+- ireq->loc_addr,
+- ntohs(inet_sk(sk)->sport),
+- ireq->rmt_addr,
+- ntohs(ireq->rmt_port),
+- TCP_SYN_RECV,
+- 0, 0, /* could print option size, but that is af dependent. */
+- 1, /* timers active (only the expire timer) */
+- jiffies_to_clock_t(ttd),
+- req->retrans,
+- uid,
+- 0, /* non standard timer */
+- 0, /* open_requests have no inode */
+- atomic_read(&sk->sk_refcnt),
+- req);
+-}
+-
+-static void get_tcp4_sock(struct sock *sk, char *tmpbuf, int i)
+-{
+- int timer_active;
+- unsigned long timer_expires;
+- struct tcp_sock *tp = tcp_sk(sk);
+- const struct inet_connection_sock *icsk = inet_csk(sk);
+- struct inet_sock *inet = inet_sk(sk);
+- __be32 dest = inet->daddr;
+- __be32 src = inet->rcv_saddr;
+- __u16 destp = ntohs(inet->dport);
+- __u16 srcp = ntohs(inet->sport);
+-
+- if (icsk->icsk_pending == ICSK_TIME_RETRANS) {
+- timer_active = 1;
+- timer_expires = icsk->icsk_timeout;
+- } else if (icsk->icsk_pending == ICSK_TIME_PROBE0) {
+- timer_active = 4;
+- timer_expires = icsk->icsk_timeout;
+- } else if (timer_pending(&sk->sk_timer)) {
+- timer_active = 2;
+- timer_expires = sk->sk_timer.expires;
+- } else {
+- timer_active = 0;
+- timer_expires = jiffies;
+- }
+-
+- sprintf(tmpbuf, "%4d: %08X:%04X %08X:%04X %02X %08X:%08X %02X:%08lX "
+- "%08X %5d %8d %lu %d %p %u %u %u %u %d",
+- i, src, srcp, dest, destp, sk->sk_state,
+- tp->write_seq - tp->snd_una,
+- sk->sk_state == TCP_LISTEN ? sk->sk_ack_backlog :
+- (tp->rcv_nxt - tp->copied_seq),
+- timer_active,
+- jiffies_to_clock_t(timer_expires - jiffies),
+- icsk->icsk_retransmits,
+- sock_i_uid(sk),
+- icsk->icsk_probes_out,
+- sock_i_ino(sk),
+- atomic_read(&sk->sk_refcnt), sk,
+- icsk->icsk_rto,
+- icsk->icsk_ack.ato,
+- (icsk->icsk_ack.quick << 1) | icsk->icsk_ack.pingpong,
+- tp->snd_cwnd,
+- tp->snd_ssthresh >= 0xFFFF ? -1 : tp->snd_ssthresh);
+-}
+-
+-static void get_timewait4_sock(struct inet_timewait_sock *tw,
+- char *tmpbuf, int i)
+-{
+- __be32 dest, src;
+- __u16 destp, srcp;
+- int ttd = tw->tw_ttd - jiffies;
+-
+- if (ttd < 0)
+- ttd = 0;
+-
+- dest = tw->tw_daddr;
+- src = tw->tw_rcv_saddr;
+- destp = ntohs(tw->tw_dport);
+- srcp = ntohs(tw->tw_sport);
+-
+- sprintf(tmpbuf, "%4d: %08X:%04X %08X:%04X"
+- " %02X %08X:%08X %02X:%08lX %08X %5d %8d %d %d %p",
+- i, src, srcp, dest, destp, tw->tw_substate, 0, 0,
+- 3, jiffies_to_clock_t(ttd), 0, 0, 0, 0,
+- atomic_read(&tw->tw_refcnt), tw);
+-}
+-
+-#define TMPSZ 150
+-
+-static int tcp4_seq_show(struct seq_file *seq, void *v)
+-{
+- struct tcp_iter_state* st;
+- char tmpbuf[TMPSZ + 1];
+-
+- if (v == SEQ_START_TOKEN) {
+- seq_printf(seq, "%-*s\n", TMPSZ - 1,
+- " sl local_address rem_address st tx_queue "
+- "rx_queue tr tm->when retrnsmt uid timeout "
+- "inode");
+- goto out;
+- }
+- st = seq->private;
+-
+- switch (st->state) {
+- case TCP_SEQ_STATE_LISTENING:
+- case TCP_SEQ_STATE_ESTABLISHED:
+- get_tcp4_sock(v, tmpbuf, st->num);
+- break;
+- case TCP_SEQ_STATE_OPENREQ:
+- get_openreq4(st->syn_wait_sk, v, tmpbuf, st->num, st->uid);
+- break;
+- case TCP_SEQ_STATE_TIME_WAIT:
+- get_timewait4_sock(v, tmpbuf, st->num);
+- break;
+- }
+- seq_printf(seq, "%-*s\n", TMPSZ - 1, tmpbuf);
+-out:
+- return 0;
+-}
+-
+-static struct file_operations tcp4_seq_fops;
+-static struct tcp_seq_afinfo tcp4_seq_afinfo = {
+- .owner = THIS_MODULE,
+- .name = "tcp",
+- .family = AF_INET,
+- .seq_show = tcp4_seq_show,
+- .seq_fops = &tcp4_seq_fops,
+-};
+-
+-int __init tcp4_proc_init(void)
+-{
+- return tcp_proc_register(&tcp4_seq_afinfo);
+-}
+-
+-void tcp4_proc_exit(void)
+-{
+- tcp_proc_unregister(&tcp4_seq_afinfo);
+-}
+-#endif /* CONFIG_PROC_FS */
+-
+-struct proto tcp_prot = {
+- .name = "TCP",
+- .owner = THIS_MODULE,
+- .close = tcp_close,
+- .connect = tcp_v4_connect,
+- .disconnect = tcp_disconnect,
+- .accept = inet_csk_accept,
+- .ioctl = tcp_ioctl,
+- .init = tcp_v4_init_sock,
+- .destroy = tcp_v4_destroy_sock,
+- .shutdown = tcp_shutdown,
+- .setsockopt = tcp_setsockopt,
+- .getsockopt = tcp_getsockopt,
+- .recvmsg = tcp_recvmsg,
+- .backlog_rcv = tcp_v4_do_rcv,
+- .hash = tcp_v4_hash,
+- .unhash = tcp_unhash,
+- .get_port = tcp_v4_get_port,
+- .enter_memory_pressure = tcp_enter_memory_pressure,
+- .sockets_allocated = &tcp_sockets_allocated,
+- .orphan_count = &tcp_orphan_count,
+- .memory_allocated = &tcp_memory_allocated,
+- .memory_pressure = &tcp_memory_pressure,
+- .sysctl_mem = sysctl_tcp_mem,
+- .sysctl_wmem = sysctl_tcp_wmem,
+- .sysctl_rmem = sysctl_tcp_rmem,
+- .max_header = MAX_TCP_HEADER,
+- .obj_size = sizeof(struct tcp_sock),
+- .twsk_prot = &tcp_timewait_sock_ops,
+- .rsk_prot = &tcp_request_sock_ops,
+-#ifdef CONFIG_COMPAT
+- .compat_setsockopt = compat_tcp_setsockopt,
+- .compat_getsockopt = compat_tcp_getsockopt,
+-#endif
+-};
+-
+-void __init tcp_v4_init(struct net_proto_family *ops)
+-{
+- if (inet_csk_ctl_sock_create(&tcp_socket, PF_INET, SOCK_RAW,
+- IPPROTO_TCP) < 0)
+- panic("Failed to create the TCP control socket.\n");
+-}
+-
+-EXPORT_SYMBOL(ipv4_specific);
+-EXPORT_SYMBOL(tcp_hashinfo);
+-EXPORT_SYMBOL(tcp_prot);
+-EXPORT_SYMBOL(tcp_unhash);
+-EXPORT_SYMBOL(tcp_v4_conn_request);
+-EXPORT_SYMBOL(tcp_v4_connect);
+-EXPORT_SYMBOL(tcp_v4_do_rcv);
+-EXPORT_SYMBOL(tcp_v4_remember_stamp);
+-EXPORT_SYMBOL(tcp_v4_send_check);
+-EXPORT_SYMBOL(tcp_v4_syn_recv_sock);
+-
+-#ifdef CONFIG_PROC_FS
+-EXPORT_SYMBOL(tcp_proc_register);
+-EXPORT_SYMBOL(tcp_proc_unregister);
+-#endif
+-EXPORT_SYMBOL(sysctl_local_port_range);
+-EXPORT_SYMBOL(sysctl_tcp_low_latency);
+-
+diff -Nurb linux-2.6.22-570/net/ipv4/tcp_output.c linux-2.6.22-590/net/ipv4/tcp_output.c
+--- linux-2.6.22-570/net/ipv4/tcp_output.c 2007-07-08 19:32:17.000000000 -0400
++++ linux-2.6.22-590/net/ipv4/tcp_output.c 2008-01-29 22:12:32.000000000 -0500
+@@ -432,11 +432,11 @@
+ sysctl_flags = 0;
+ if (unlikely(tcb->flags & TCPCB_FLAG_SYN)) {
+ tcp_header_size = sizeof(struct tcphdr) + TCPOLEN_MSS;
+- if (sysctl_tcp_timestamps) {
++ if (sk->sk_net->sysctl_tcp_timestamps) {
+ tcp_header_size += TCPOLEN_TSTAMP_ALIGNED;
+ sysctl_flags |= SYSCTL_FLAG_TSTAMPS;
+ }
+- if (sysctl_tcp_window_scaling) {
++ if (sk->sk_net->sysctl_tcp_window_scaling) {
+ tcp_header_size += TCPOLEN_WSCALE_ALIGNED;
+ sysctl_flags |= SYSCTL_FLAG_WSCALE;
+ }
+@@ -2215,7 +2215,7 @@
+ * See tcp_input.c:tcp_rcv_state_process case TCP_SYN_SENT.
+ */
+ tp->tcp_header_len = sizeof(struct tcphdr) +
+- (sysctl_tcp_timestamps ? TCPOLEN_TSTAMP_ALIGNED : 0);
++ (sk->sk_net->sysctl_tcp_timestamps ? TCPOLEN_TSTAMP_ALIGNED : 0);
+
+ #ifdef CONFIG_TCP_MD5SIG
+ if (tp->af_specific->md5_lookup(sk, sk) != NULL)
+@@ -2238,7 +2238,7 @@
+ tp->advmss - (tp->rx_opt.ts_recent_stamp ? tp->tcp_header_len - sizeof(struct tcphdr) : 0),
+ &tp->rcv_wnd,
+ &tp->window_clamp,
+- sysctl_tcp_window_scaling,
++ sk->sk_net->sysctl_tcp_window_scaling,
+ &rcv_wscale);
+
+ tp->rx_opt.rcv_wscale = rcv_wscale;
+diff -Nurb linux-2.6.22-570/net/ipv4/tcp_probe.c linux-2.6.22-590/net/ipv4/tcp_probe.c
+--- linux-2.6.22-570/net/ipv4/tcp_probe.c 2007-07-08 19:32:17.000000000 -0400
++++ linux-2.6.22-590/net/ipv4/tcp_probe.c 2008-01-29 22:12:32.000000000 -0500
+@@ -172,7 +172,7 @@
+ if (IS_ERR(tcpw.fifo))
+ return PTR_ERR(tcpw.fifo);
+
+- if (!proc_net_fops_create(procname, S_IRUSR, &tcpprobe_fops))
++ if (!proc_net_fops_create(&init_net, procname, S_IRUSR, &tcpprobe_fops))
+ goto err0;
+
+ ret = register_jprobe(&tcp_probe);
+@@ -182,7 +182,7 @@
+ pr_info("TCP watch registered (port=%d)\n", port);
+ return 0;
+ err1:
+- proc_net_remove(procname);
++ proc_net_remove(&init_net, procname);
+ err0:
+ kfifo_free(tcpw.fifo);
+ return ret;
+@@ -192,7 +192,7 @@
+ static __exit void tcpprobe_exit(void)
+ {
+ kfifo_free(tcpw.fifo);
+- proc_net_remove(procname);
++ proc_net_remove(&init_net, procname);
+ unregister_jprobe(&tcp_probe);
+
+ }
+diff -Nurb linux-2.6.22-570/net/ipv4/tunnel4.c linux-2.6.22-590/net/ipv4/tunnel4.c
+--- linux-2.6.22-570/net/ipv4/tunnel4.c 2007-07-08 19:32:17.000000000 -0400
++++ linux-2.6.22-590/net/ipv4/tunnel4.c 2008-01-29 22:12:32.000000000 -0500
+@@ -75,6 +75,10 @@
+ {
+ struct xfrm_tunnel *handler;
+
++ if (skb->dev->nd_net != &init_net) {
++ kfree_skb(skb);
++ return 0;
++ }
+ if (!pskb_may_pull(skb, sizeof(struct iphdr)))
+ goto drop;
+
+@@ -113,6 +117,9 @@
+ {
+ struct xfrm_tunnel *handler;
+
++ if (skb->dev->nd_net != &init_net)
++ return;
++
+ for (handler = tunnel4_handlers; handler; handler = handler->next)
+ if (!handler->err_handler(skb, info))
+ break;
+diff -Nurb linux-2.6.22-570/net/ipv4/udp.c linux-2.6.22-590/net/ipv4/udp.c
+--- linux-2.6.22-570/net/ipv4/udp.c 2008-01-29 22:12:23.000000000 -0500
++++ linux-2.6.22-590/net/ipv4/udp.c 2008-01-29 22:12:32.000000000 -0500
+@@ -101,6 +101,7 @@
+ #include <net/route.h>
+ #include <net/checksum.h>
+ #include <net/xfrm.h>
++#include <net/net_namespace.h>
+ #include "udp_impl.h"
+
+ /*
+@@ -112,16 +113,17 @@
+ struct hlist_head udp_hash[UDP_HTABLE_SIZE];
+ DEFINE_RWLOCK(udp_hash_lock);
+
+-static int udp_port_rover;
+-
+-static inline int __udp_lib_lport_inuse(__u16 num, struct hlist_head udptable[])
++static inline int __udp_lib_lport_inuse(struct net *net, __u16 num, struct hlist_head udptable[])
+ {
+ struct sock *sk;
+ struct hlist_node *node;
+
+- sk_for_each(sk, node, &udptable[num & (UDP_HTABLE_SIZE - 1)])
++ sk_for_each(sk, node, &udptable[num & (UDP_HTABLE_SIZE - 1)]) {
++ if (sk->sk_net != net)
++ continue;
+ if (sk->sk_hash == num)
+ return 1;
++ }
+ return 0;
+ }
+
+@@ -148,9 +150,9 @@
+ if (snum == 0) {
+ int best_size_so_far, best, result, i;
+
+- if (*port_rover > sysctl_local_port_range[1] ||
+- *port_rover < sysctl_local_port_range[0])
+- *port_rover = sysctl_local_port_range[0];
++ if (*port_rover > sk->sk_net->sysctl_local_port_range[1] ||
++ *port_rover < sk->sk_net->sysctl_local_port_range[0])
++ *port_rover = sk->sk_net->sysctl_local_port_range[0];
+ best_size_so_far = 32767;
+ best = result = *port_rover;
+ for (i = 0; i < UDP_HTABLE_SIZE; i++, result++) {
+@@ -158,9 +160,9 @@
+
+ head = &udptable[result & (UDP_HTABLE_SIZE - 1)];
+ if (hlist_empty(head)) {
+- if (result > sysctl_local_port_range[1])
+- result = sysctl_local_port_range[0] +
+- ((result - sysctl_local_port_range[0]) &
++ if (result > sk->sk_net->sysctl_local_port_range[1])
++ result = sk->sk_net->sysctl_local_port_range[0] +
++ ((result - sk->sk_net->sysctl_local_port_range[0]) &
+ (UDP_HTABLE_SIZE - 1));
+ goto gotit;
+ }
+@@ -177,11 +179,11 @@
+ result = best;
+ for (i = 0; i < (1 << 16) / UDP_HTABLE_SIZE;
+ i++, result += UDP_HTABLE_SIZE) {
+- if (result > sysctl_local_port_range[1])
+- result = sysctl_local_port_range[0]
+- + ((result - sysctl_local_port_range[0]) &
++ if (result > sk->sk_net->sysctl_local_port_range[1])
++ result = sk->sk_net->sysctl_local_port_range[0]
++ + ((result - sk->sk_net->sysctl_local_port_range[0]) &
+ (UDP_HTABLE_SIZE - 1));
+- if (! __udp_lib_lport_inuse(result, udptable))
++ if (! __udp_lib_lport_inuse(sk->sk_net, result, udptable))
+ break;
+ }
+ if (i >= (1 << 16) / UDP_HTABLE_SIZE)
+@@ -194,6 +196,7 @@
+ sk_for_each(sk2, node, head)
+ if (sk2->sk_hash == snum &&
+ sk2 != sk &&
++ sk->sk_net == sk2->sk_net &&
+ (!sk2->sk_reuse || !sk->sk_reuse) &&
+ (!sk2->sk_bound_dev_if || !sk->sk_bound_dev_if
+ || sk2->sk_bound_dev_if == sk->sk_bound_dev_if) &&
+@@ -216,7 +219,7 @@
+ int udp_get_port(struct sock *sk, unsigned short snum,
+ int (*scmp)(const struct sock *, const struct sock *))
+ {
+- return __udp_lib_get_port(sk, snum, udp_hash, &udp_port_rover, scmp);
++ return __udp_lib_get_port(sk, snum, udp_hash, &sk->sk_net->udp_port_rover, scmp);
+ }
+
+ extern int ipv4_rcv_saddr_equal(const struct sock *sk1, const struct sock *sk2);
+@@ -229,7 +232,8 @@
+ /* UDP is nearly always wildcards out the wazoo, it makes no sense to try
+ * harder than this. -DaveM
+ */
+-static struct sock *__udp4_lib_lookup(__be32 saddr, __be16 sport,
++static struct sock *__udp4_lib_lookup(struct net *net,
++ __be32 saddr, __be16 sport,
+ __be32 daddr, __be16 dport,
+ int dif, struct hlist_head udptable[])
+ {
+@@ -243,6 +247,9 @@
+ sk_for_each(sk, node, &udptable[hnum & (UDP_HTABLE_SIZE - 1)]) {
+ struct inet_sock *inet = inet_sk(sk);
+
++ if (sk->sk_net != net)
++ continue;
++
+ if (sk->sk_hash == hnum && !ipv6_only_sock(sk)) {
+ int score = (sk->sk_family == PF_INET ? 1 : 0);
+
+@@ -299,6 +306,9 @@
+ sk_for_each_from(s, node) {
+ struct inet_sock *inet = inet_sk(s);
+
++ if (s->sk_net != sk->sk_net)
++ continue;
++
+ if (s->sk_hash != hnum ||
+ (inet->daddr && inet->daddr != rmt_addr) ||
+ (inet->dport != rmt_port && inet->dport) ||
+@@ -328,6 +338,7 @@
+
+ void __udp4_lib_err(struct sk_buff *skb, u32 info, struct hlist_head udptable[])
+ {
++ struct net *net = skb->dev->nd_net;
+ struct inet_sock *inet;
+ struct iphdr *iph = (struct iphdr*)skb->data;
+ struct udphdr *uh = (struct udphdr*)(skb->data+(iph->ihl<<2));
+@@ -337,7 +348,7 @@
+ int harderr;
+ int err;
+
+- sk = __udp4_lib_lookup(iph->daddr, uh->dest, iph->saddr, uh->source,
++ sk = __udp4_lib_lookup(net, iph->daddr, uh->dest, iph->saddr, uh->source,
+ skb->dev->ifindex, udptable );
+ if (sk == NULL) {
+ ICMP_INC_STATS_BH(ICMP_MIB_INERRORS);
+@@ -623,7 +634,8 @@
+ rt = (struct rtable*)sk_dst_check(sk, 0);
+
+ if (rt == NULL) {
+- struct flowi fl = { .oif = ipc.oif,
++ struct flowi fl = { .fl_net = sk->sk_net,
++ .oif = ipc.oif,
+ .nl_u = { .ip4_u =
+ { .daddr = faddr,
+ .saddr = saddr,
+@@ -1288,6 +1300,7 @@
+ int __udp4_lib_rcv(struct sk_buff *skb, struct hlist_head udptable[],
+ int proto)
+ {
++ struct net *net = skb->dev->nd_net;
+ struct sock *sk;
+ struct udphdr *uh = udp_hdr(skb);
+ unsigned short ulen;
+@@ -1322,7 +1335,7 @@
+ udp_ping_of_death(skb, uh, saddr);
+ #endif
+
+- sk = __udp4_lib_lookup(saddr, uh->source, daddr, uh->dest,
++ sk = __udp4_lib_lookup(net, saddr, uh->source, daddr, uh->dest,
+ skb->dev->ifindex, udptable );
+
+ if (sk != NULL) {
+@@ -1651,7 +1664,7 @@
+ sk = sk_next(sk);
+ try_again:
+ ;
+- } while (sk && (sk->sk_family != state->family ||
++ } while (sk && ((sk->sk_net != state->net) || sk->sk_family != state->family ||
+ !nx_check(sk->sk_nid, VS_WATCH_P | VS_IDENT)));
+
+ if (!sk && ++state->bucket < UDP_HTABLE_SIZE) {
+@@ -1717,6 +1730,7 @@
+
+ seq = file->private_data;
+ seq->private = s;
++ s->net = get_net(PROC_NET(inode));
+ out:
+ return rc;
+ out_kfree:
+@@ -1724,21 +1738,31 @@
+ goto out;
+ }
+
++static int udp_seq_release(struct inode *inode, struct file *file)
++{
++ struct seq_file *seq = file->private_data;
++ struct udp_iter_state *state = seq->private;
++ put_net(state->net);
++ return seq_release_private(inode, file);
++}
++
+ /* ------------------------------------------------------------------------ */
+-int udp_proc_register(struct udp_seq_afinfo *afinfo)
++int udp_proc_register(struct net *net, struct udp_seq_afinfo *afinfo)
+ {
+ struct proc_dir_entry *p;
+ int rc = 0;
+
+ if (!afinfo)
+ return -EINVAL;
++ if (net == &init_net) {
+ afinfo->seq_fops->owner = afinfo->owner;
+ afinfo->seq_fops->open = udp_seq_open;
+ afinfo->seq_fops->read = seq_read;
+ afinfo->seq_fops->llseek = seq_lseek;
+- afinfo->seq_fops->release = seq_release_private;
++ afinfo->seq_fops->release = udp_seq_release;
++ }
+
+- p = proc_net_fops_create(afinfo->name, S_IRUGO, afinfo->seq_fops);
++ p = proc_net_fops_create(net, afinfo->name, S_IRUGO, afinfo->seq_fops);
+ if (p)
+ p->data = afinfo;
+ else
+@@ -1746,11 +1770,12 @@
+ return rc;
+ }
+
+-void udp_proc_unregister(struct udp_seq_afinfo *afinfo)
++void udp_proc_unregister(struct net *net, struct udp_seq_afinfo *afinfo)
+ {
+ if (!afinfo)
+ return;
+- proc_net_remove(afinfo->name);
++ proc_net_remove(net, afinfo->name);
++ if (net == &init_net)
+ memset(afinfo->seq_fops, 0, sizeof(*afinfo->seq_fops));
+ }
+
+@@ -1803,14 +1828,30 @@
+ .seq_fops = &udp4_seq_fops,
+ };
+
++
++static int udp4_proc_net_init(struct net *net)
++{
++ return udp_proc_register(net, &udp4_seq_afinfo);
++}
++
++static void udp4_proc_net_exit(struct net *net)
++{
++ udp_proc_unregister(net, &udp4_seq_afinfo);
++}
++
++static struct pernet_operations udp4_proc_net_ops = {
++ .init = udp4_proc_net_init,
++ .exit = udp4_proc_net_exit,
++};
++
+ int __init udp4_proc_init(void)
+ {
+- return udp_proc_register(&udp4_seq_afinfo);
++ return register_pernet_subsys(&udp4_proc_net_ops);
+ }
+
+ void udp4_proc_exit(void)
+ {
+- udp_proc_unregister(&udp4_seq_afinfo);
++ unregister_pernet_subsys(&udp4_proc_net_ops);
+ }
+ #endif /* CONFIG_PROC_FS */
+
+diff -Nurb linux-2.6.22-570/net/ipv4/udplite.c linux-2.6.22-590/net/ipv4/udplite.c
+--- linux-2.6.22-570/net/ipv4/udplite.c 2007-07-08 19:32:17.000000000 -0400
++++ linux-2.6.22-590/net/ipv4/udplite.c 2008-01-29 22:12:32.000000000 -0500
+@@ -31,11 +31,18 @@
+
+ static int udplite_rcv(struct sk_buff *skb)
+ {
++ if (skb->dev->nd_net != &init_net) {
++ kfree_skb(skb);
++ return 0;
++ }
+ return __udp4_lib_rcv(skb, udplite_hash, IPPROTO_UDPLITE);
+ }
+
+ static void udplite_err(struct sk_buff *skb, u32 info)
+ {
++ if (skb->dev->nd_net != &init_net)
++ return;
++
+ return __udp4_lib_err(skb, info, udplite_hash);
+ }
+
+@@ -103,7 +110,7 @@
+ inet_register_protosw(&udplite4_protosw);
+
+ #ifdef CONFIG_PROC_FS
+- if (udp_proc_register(&udplite4_seq_afinfo)) /* udplite4_proc_init() */
++ if (udp_proc_register(&init_net, &udplite4_seq_afinfo)) /* udplite4_proc_init() */
+ printk(KERN_ERR "%s: Cannot register /proc!\n", __FUNCTION__);
+ #endif
+ return;
+diff -Nurb linux-2.6.22-570/net/ipv4/xfrm4_input.c linux-2.6.22-590/net/ipv4/xfrm4_input.c
+--- linux-2.6.22-570/net/ipv4/xfrm4_input.c 2007-07-08 19:32:17.000000000 -0400
++++ linux-2.6.22-590/net/ipv4/xfrm4_input.c 2008-01-29 22:12:32.000000000 -0500
+@@ -18,6 +18,10 @@
+
+ int xfrm4_rcv(struct sk_buff *skb)
+ {
++ if (skb->dev->nd_net != &init_net) {
++ kfree_skb(skb);
++ return 0;
++ }
+ return xfrm4_rcv_encap(skb, 0);
+ }
+
+diff -Nurb linux-2.6.22-570/net/ipv4/xfrm4_policy.c linux-2.6.22-590/net/ipv4/xfrm4_policy.c
+--- linux-2.6.22-570/net/ipv4/xfrm4_policy.c 2007-07-08 19:32:17.000000000 -0400
++++ linux-2.6.22-590/net/ipv4/xfrm4_policy.c 2008-01-29 22:12:32.000000000 -0500
+@@ -25,6 +25,7 @@
+ {
+ struct rtable *rt;
+ struct flowi fl_tunnel = {
++ .fl_net = &init_net,
+ .nl_u = {
+ .ip4_u = {
+ .daddr = daddr->a4,
+@@ -73,6 +74,7 @@
+ struct rtable *rt0 = (struct rtable*)(*dst_p);
+ struct rtable *rt = rt0;
+ struct flowi fl_tunnel = {
++ .fl_net = &init_net,
+ .nl_u = {
+ .ip4_u = {
+ .saddr = fl->fl4_src,
+@@ -213,6 +215,7 @@
+ u8 *xprth = skb_network_header(skb) + iph->ihl * 4;
+
+ memset(fl, 0, sizeof(struct flowi));
++ fl->fl_net = &init_net;
+ if (!(iph->frag_off & htons(IP_MF | IP_OFFSET))) {
+ switch (iph->protocol) {
+ case IPPROTO_UDP:
+@@ -306,7 +309,7 @@
+
+ xdst = (struct xfrm_dst *)dst;
+ if (xdst->u.rt.idev->dev == dev) {
+- struct in_device *loopback_idev = in_dev_get(&loopback_dev);
++ struct in_device *loopback_idev = in_dev_get(&init_net.loopback_dev);
+ BUG_ON(!loopback_idev);
+
+ do {
+diff -Nurb linux-2.6.22-570/net/ipv4/xfrm4_state.c linux-2.6.22-590/net/ipv4/xfrm4_state.c
+--- linux-2.6.22-570/net/ipv4/xfrm4_state.c 2007-07-08 19:32:17.000000000 -0400
++++ linux-2.6.22-590/net/ipv4/xfrm4_state.c 2008-01-29 22:12:32.000000000 -0500
+@@ -16,7 +16,7 @@
+
+ static int xfrm4_init_flags(struct xfrm_state *x)
+ {
+- if (ipv4_config.no_pmtu_disc)
++ if (init_net.sysctl_ipv4_no_pmtu_disc)
+ x->props.flags |= XFRM_STATE_NOPMTUDISC;
+ return 0;
+ }
+diff -Nurb linux-2.6.22-570/net/ipv4/xfrm4_tunnel.c linux-2.6.22-590/net/ipv4/xfrm4_tunnel.c
+--- linux-2.6.22-570/net/ipv4/xfrm4_tunnel.c 2007-07-08 19:32:17.000000000 -0400
++++ linux-2.6.22-590/net/ipv4/xfrm4_tunnel.c 2008-01-29 22:12:32.000000000 -0500
+@@ -109,3 +109,4 @@
+ module_init(ipip_init);
+ module_exit(ipip_fini);
+ MODULE_LICENSE("GPL");
++MODULE_ALIAS_XFRM_TYPE(AF_INET, XFRM_PROTO_IPIP);
+diff -Nurb linux-2.6.22-570/net/ipv6/Kconfig linux-2.6.22-590/net/ipv6/Kconfig
+--- linux-2.6.22-570/net/ipv6/Kconfig 2008-01-29 22:12:21.000000000 -0500
++++ linux-2.6.22-590/net/ipv6/Kconfig 2008-01-29 22:12:32.000000000 -0500
+@@ -109,7 +109,7 @@
+ If unsure, say Y.
+
+ config IPV6_MIP6
+- bool "IPv6: Mobility (EXPERIMENTAL)"
++ tristate "IPv6: Mobility (EXPERIMENTAL)"
+ depends on IPV6 && EXPERIMENTAL
+ select XFRM
+ ---help---
+diff -Nurb linux-2.6.22-570/net/ipv6/Makefile linux-2.6.22-590/net/ipv6/Makefile
+--- linux-2.6.22-570/net/ipv6/Makefile 2007-07-08 19:32:17.000000000 -0400
++++ linux-2.6.22-590/net/ipv6/Makefile 2008-01-29 22:12:32.000000000 -0500
+@@ -14,7 +14,6 @@
+ xfrm6_output.o
+ ipv6-$(CONFIG_NETFILTER) += netfilter.o
+ ipv6-$(CONFIG_IPV6_MULTIPLE_TABLES) += fib6_rules.o
+-ipv6-$(CONFIG_IPV6_MIP6) += mip6.o
+ ipv6-$(CONFIG_PROC_FS) += proc.o
+
+ ipv6-objs += $(ipv6-y)
+@@ -28,6 +27,7 @@
+ obj-$(CONFIG_INET6_XFRM_MODE_TUNNEL) += xfrm6_mode_tunnel.o
+ obj-$(CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION) += xfrm6_mode_ro.o
+ obj-$(CONFIG_INET6_XFRM_MODE_BEET) += xfrm6_mode_beet.o
++obj-$(CONFIG_IPV6_MIP6) += mip6.o
+ obj-$(CONFIG_NETFILTER) += netfilter/
+
+ obj-$(CONFIG_IPV6_SIT) += sit.o
+diff -Nurb linux-2.6.22-570/net/ipv6/addrconf.c linux-2.6.22-590/net/ipv6/addrconf.c
+--- linux-2.6.22-570/net/ipv6/addrconf.c 2008-01-29 22:12:21.000000000 -0500
++++ linux-2.6.22-590/net/ipv6/addrconf.c 2008-01-29 22:12:32.000000000 -0500
+@@ -73,6 +73,7 @@
+ #include <net/tcp.h>
+ #include <net/ip.h>
+ #include <net/netlink.h>
++#include <net/net_namespace.h>
+ #include <net/pkt_sched.h>
+ #include <linux/if_tunnel.h>
+ #include <linux/rtnetlink.h>
+@@ -457,7 +458,7 @@
+ struct inet6_dev *idev;
+
+ read_lock(&dev_base_lock);
+- for_each_netdev(dev) {
++ for_each_netdev(&init_net, dev) {
+ rcu_read_lock();
+ idev = __in6_dev_get(dev);
+ if (idev) {
+@@ -920,7 +921,7 @@
+ read_lock(&dev_base_lock);
+ rcu_read_lock();
+
+- for_each_netdev(dev) {
++ for_each_netdev(&init_net, dev) {
+ struct inet6_dev *idev;
+ struct inet6_ifaddr *ifa;
+
+@@ -1047,7 +1048,7 @@
+ }
+
+ /* Rule 4: Prefer home address */
+-#ifdef CONFIG_IPV6_MIP6
++#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
+ if (hiscore.rule < 4) {
+ if (ifa_result->flags & IFA_F_HOMEADDRESS)
+ hiscore.attrs |= IPV6_SADDR_SCORE_HOA;
+@@ -1882,7 +1883,7 @@
+ if (copy_from_user(&ireq, arg, sizeof(struct in6_ifreq)))
+ goto err_exit;
+
+- dev = __dev_get_by_index(ireq.ifr6_ifindex);
++ dev = __dev_get_by_index(&init_net, ireq.ifr6_ifindex);
+
+ err = -ENODEV;
+ if (dev == NULL)
+@@ -1913,7 +1914,7 @@
+
+ if (err == 0) {
+ err = -ENOBUFS;
+- if ((dev = __dev_get_by_name(p.name)) == NULL)
++ if ((dev = __dev_get_by_name(&init_net, p.name)) == NULL)
+ goto err_exit;
+ err = dev_open(dev);
+ }
+@@ -1943,7 +1944,7 @@
+ if (!valid_lft || prefered_lft > valid_lft)
+ return -EINVAL;
+
+- if ((dev = __dev_get_by_index(ifindex)) == NULL)
++ if ((dev = __dev_get_by_index(&init_net, ifindex)) == NULL)
+ return -ENODEV;
+
+ if ((idev = addrconf_add_dev(dev)) == NULL)
+@@ -1994,7 +1995,7 @@
+ struct inet6_dev *idev;
+ struct net_device *dev;
+
+- if ((dev = __dev_get_by_index(ifindex)) == NULL)
++ if ((dev = __dev_get_by_index(&init_net, ifindex)) == NULL)
+ return -ENODEV;
+
+ if ((idev = __in6_dev_get(dev)) == NULL)
+@@ -2089,7 +2090,7 @@
+ return;
+ }
+
+- for_each_netdev(dev) {
++ for_each_netdev(&init_net, dev) {
+ struct in_device * in_dev = __in_dev_get_rtnl(dev);
+ if (in_dev && (dev->flags & IFF_UP)) {
+ struct in_ifaddr * ifa;
+@@ -2245,12 +2246,12 @@
+
+ /* first try to inherit the link-local address from the link device */
+ if (idev->dev->iflink &&
+- (link_dev = __dev_get_by_index(idev->dev->iflink))) {
++ (link_dev = __dev_get_by_index(&init_net, idev->dev->iflink))) {
+ if (!ipv6_inherit_linklocal(idev, link_dev))
+ return;
+ }
+ /* then try to inherit it from any device */
+- for_each_netdev(link_dev) {
++ for_each_netdev(&init_net, link_dev) {
+ if (!ipv6_inherit_linklocal(idev, link_dev))
+ return;
+ }
+@@ -2282,6 +2283,9 @@
+ struct inet6_dev *idev = __in6_dev_get(dev);
+ int run_pending = 0;
+
++ if (dev->nd_net != &init_net)
++ return NOTIFY_DONE;
++
+ switch(event) {
+ case NETDEV_REGISTER:
+ if (!idev && dev->mtu >= IPV6_MIN_MTU) {
+@@ -2419,7 +2423,7 @@
+
+ ASSERT_RTNL();
+
+- if (dev == &loopback_dev && how == 1)
++ if (dev == &init_net.loopback_dev && how == 1)
+ how = 0;
+
+ rt6_ifdown(dev);
+@@ -2850,18 +2854,18 @@
+
+ int __init if6_proc_init(void)
+ {
+- if (!proc_net_fops_create("if_inet6", S_IRUGO, &if6_fops))
++ if (!proc_net_fops_create(&init_net, "if_inet6", S_IRUGO, &if6_fops))
+ return -ENOMEM;
+ return 0;
+ }
+
+ void if6_proc_exit(void)
+ {
+- proc_net_remove("if_inet6");
++ proc_net_remove(&init_net, "if_inet6");
+ }
+ #endif /* CONFIG_PROC_FS */
+
+-#ifdef CONFIG_IPV6_MIP6
++#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
+ /* Check if address is a home address configured on any interface. */
+ int ipv6_chk_home_addr(struct in6_addr *addr)
+ {
+@@ -3017,11 +3021,15 @@
+ static int
+ inet6_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+ {
++ struct net *net = skb->sk->sk_net;
+ struct ifaddrmsg *ifm;
+ struct nlattr *tb[IFA_MAX+1];
+ struct in6_addr *pfx;
+ int err;
+
++ if (net != &init_net)
++ return -EINVAL;
++
+ err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv6_policy);
+ if (err < 0)
+ return err;
+@@ -3074,6 +3082,7 @@
+ static int
+ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+ {
++ struct net *net = skb->sk->sk_net;
+ struct ifaddrmsg *ifm;
+ struct nlattr *tb[IFA_MAX+1];
+ struct in6_addr *pfx;
+@@ -3083,6 +3092,9 @@
+ u8 ifa_flags;
+ int err;
+
++ if (net != &init_net)
++ return -EINVAL;
++
+ err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv6_policy);
+ if (err < 0)
+ return err;
+@@ -3103,7 +3115,7 @@
+ valid_lft = INFINITY_LIFE_TIME;
+ }
+
+- dev = __dev_get_by_index(ifm->ifa_index);
++ dev = __dev_get_by_index(&init_net, ifm->ifa_index);
+ if (dev == NULL)
+ return -ENODEV;
+
+@@ -3292,7 +3304,7 @@
+ s_ip_idx = ip_idx = cb->args[1];
+
+ idx = 0;
+- for_each_netdev(dev) {
++ for_each_netdev(&init_net, dev) {
+ if (idx < s_idx)
+ goto cont;
+ if (idx > s_idx)
+@@ -3367,26 +3379,42 @@
+
+ static int inet6_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb)
+ {
++ struct net *net = skb->sk->sk_net;
+ enum addr_type_t type = UNICAST_ADDR;
++
++ if (net != &init_net)
++ return 0;
++
+ return inet6_dump_addr(skb, cb, type);
+ }
+
+ static int inet6_dump_ifmcaddr(struct sk_buff *skb, struct netlink_callback *cb)
+ {
++ struct net *net = skb->sk->sk_net;
+ enum addr_type_t type = MULTICAST_ADDR;
++
++ if (net != &init_net)
++ return 0;
++
+ return inet6_dump_addr(skb, cb, type);
+ }
+
+
+ static int inet6_dump_ifacaddr(struct sk_buff *skb, struct netlink_callback *cb)
+ {
++ struct net *net = skb->sk->sk_net;
+ enum addr_type_t type = ANYCAST_ADDR;
++
++ if (net != &init_net)
++ return 0;
++
+ return inet6_dump_addr(skb, cb, type);
+ }
+
+ static int inet6_rtm_getaddr(struct sk_buff *in_skb, struct nlmsghdr* nlh,
+ void *arg)
+ {
++ struct net *net = in_skb->sk->sk_net;
+ struct ifaddrmsg *ifm;
+ struct nlattr *tb[IFA_MAX+1];
+ struct in6_addr *addr = NULL;
+@@ -3395,6 +3423,9 @@
+ struct sk_buff *skb;
+ int err;
+
++ if (net != &init_net)
++ return -EINVAL;
++
+ err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv6_policy);
+ if (err < 0)
+ goto errout;
+@@ -3407,7 +3438,7 @@
+
+ ifm = nlmsg_data(nlh);
+ if (ifm->ifa_index)
+- dev = __dev_get_by_index(ifm->ifa_index);
++ dev = __dev_get_by_index(&init_net, ifm->ifa_index);
+
+ if ((ifa = ipv6_get_ifaddr(addr, dev, 1)) == NULL) {
+ err = -EADDRNOTAVAIL;
+@@ -3427,7 +3458,7 @@
+ kfree_skb(skb);
+ goto errout_ifa;
+ }
+- err = rtnl_unicast(skb, NETLINK_CB(in_skb).pid);
++ err = rtnl_unicast(skb, &init_net, NETLINK_CB(in_skb).pid);
+ errout_ifa:
+ in6_ifa_put(ifa);
+ errout:
+@@ -3450,10 +3481,10 @@
+ kfree_skb(skb);
+ goto errout;
+ }
+- err = rtnl_notify(skb, 0, RTNLGRP_IPV6_IFADDR, NULL, GFP_ATOMIC);
++ err = rtnl_notify(skb, &init_net, 0, RTNLGRP_IPV6_IFADDR, NULL, GFP_ATOMIC);
+ errout:
+ if (err < 0)
+- rtnl_set_sk_err(RTNLGRP_IPV6_IFADDR, err);
++ rtnl_set_sk_err(&init_net, RTNLGRP_IPV6_IFADDR, err);
+ }
+
+ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf,
+@@ -3612,19 +3643,22 @@
+
+ static int inet6_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
+ {
++ struct net *net = skb->sk->sk_net;
+ int idx, err;
+ int s_idx = cb->args[0];
+ struct net_device *dev;
+ struct inet6_dev *idev;
+ struct nx_info *nxi = skb->sk ? skb->sk->sk_nx_info : NULL;
+
++ if (net != &init_net)
++ return 0;
+ /* FIXME: maybe disable ipv6 on non v6 guests?
+ if (skb->sk && skb->sk->sk_vx_info)
+ return skb->len; */
+
+ read_lock(&dev_base_lock);
+ idx = 0;
+- for_each_netdev(dev) {
++ for_each_netdev(&init_net, dev) {
+ if (idx < s_idx)
+ goto cont;
+ if (!v6_dev_in_nx_info(dev, nxi))
+@@ -3661,10 +3695,10 @@
+ kfree_skb(skb);
+ goto errout;
+ }
+- err = rtnl_notify(skb, 0, RTNLGRP_IPV6_IFADDR, NULL, GFP_ATOMIC);
++ err = rtnl_notify(skb, &init_net, 0, RTNLGRP_IPV6_IFADDR, NULL, GFP_ATOMIC);
+ errout:
+ if (err < 0)
+- rtnl_set_sk_err(RTNLGRP_IPV6_IFADDR, err);
++ rtnl_set_sk_err(&init_net, RTNLGRP_IPV6_IFADDR, err);
+ }
+
+ static inline size_t inet6_prefix_nlmsg_size(void)
+@@ -3730,10 +3764,10 @@
+ kfree_skb(skb);
+ goto errout;
+ }
+- err = rtnl_notify(skb, 0, RTNLGRP_IPV6_PREFIX, NULL, GFP_ATOMIC);
++ err = rtnl_notify(skb, &init_net, 0, RTNLGRP_IPV6_PREFIX, NULL, GFP_ATOMIC);
+ errout:
+ if (err < 0)
+- rtnl_set_sk_err(RTNLGRP_IPV6_PREFIX, err);
++ rtnl_set_sk_err(&init_net, RTNLGRP_IPV6_PREFIX, err);
+ }
+
+ static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)
+@@ -4244,16 +4278,16 @@
+ * device and it being up should be removed.
+ */
+ rtnl_lock();
+- if (!ipv6_add_dev(&loopback_dev))
++ if (!ipv6_add_dev(&init_net.loopback_dev))
+ err = -ENOMEM;
+ rtnl_unlock();
+ if (err)
+ return err;
+
+- ip6_null_entry.rt6i_idev = in6_dev_get(&loopback_dev);
++ ip6_null_entry.rt6i_idev = in6_dev_get(&init_net.loopback_dev);
+ #ifdef CONFIG_IPV6_MULTIPLE_TABLES
+- ip6_prohibit_entry.rt6i_idev = in6_dev_get(&loopback_dev);
+- ip6_blk_hole_entry.rt6i_idev = in6_dev_get(&loopback_dev);
++ ip6_prohibit_entry.rt6i_idev = in6_dev_get(&init_net.loopback_dev);
++ ip6_blk_hole_entry.rt6i_idev = in6_dev_get(&init_net.loopback_dev);
+ #endif
+
+ register_netdevice_notifier(&ipv6_dev_notf);
+@@ -4304,12 +4338,12 @@
+ * clean dev list.
+ */
+
+- for_each_netdev(dev) {
++ for_each_netdev(&init_net, dev) {
+ if ((idev = __in6_dev_get(dev)) == NULL)
+ continue;
+ addrconf_ifdown(dev, 1);
+ }
+- addrconf_ifdown(&loopback_dev, 2);
++ addrconf_ifdown(&init_net.loopback_dev, 2);
+
+ /*
+ * Check hash table.
+@@ -4335,6 +4369,6 @@
+ rtnl_unlock();
+
+ #ifdef CONFIG_PROC_FS
+- proc_net_remove("if_inet6");
++ proc_net_remove(&init_net, "if_inet6");
+ #endif
+ }
+diff -Nurb linux-2.6.22-570/net/ipv6/addrconf.c.orig linux-2.6.22-590/net/ipv6/addrconf.c.orig
+--- linux-2.6.22-570/net/ipv6/addrconf.c.orig 2008-01-29 22:12:18.000000000 -0500
++++ linux-2.6.22-590/net/ipv6/addrconf.c.orig 1969-12-31 19:00:00.000000000 -0500
+@@ -1,4301 +0,0 @@
+-/*
+- * IPv6 Address [auto]configuration
+- * Linux INET6 implementation
+- *
+- * Authors:
+- * Pedro Roque <roque@di.fc.ul.pt>
+- * Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
+- *
+- * $Id: addrconf.c,v 1.69 2001/10/31 21:55:54 davem Exp $
+- *
+- * 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.
+- */
+-
+-/*
+- * Changes:
+- *
+- * Janos Farkas : delete timer on ifdown
+- * <chexum@bankinf.banki.hu>
+- * Andi Kleen : kill double kfree on module
+- * unload.
+- * Maciej W. Rozycki : FDDI support
+- * sekiya@USAGI : Don't send too many RS
+- * packets.
+- * yoshfuji@USAGI : Fixed interval between DAD
+- * packets.
+- * YOSHIFUJI Hideaki @USAGI : improved accuracy of
+- * address validation timer.
+- * YOSHIFUJI Hideaki @USAGI : Privacy Extensions (RFC3041)
+- * support.
+- * Yuji SEKIYA @USAGI : Don't assign a same IPv6
+- * address on a same interface.
+- * YOSHIFUJI Hideaki @USAGI : ARCnet support
+- * YOSHIFUJI Hideaki @USAGI : convert /proc/net/if_inet6 to
+- * seq_file.
+- * YOSHIFUJI Hideaki @USAGI : improved source address
+- * selection; consider scope,
+- * status etc.
+- */
+-
+-#include <linux/errno.h>
+-#include <linux/types.h>
+-#include <linux/socket.h>
+-#include <linux/sockios.h>
+-#include <linux/net.h>
+-#include <linux/in6.h>
+-#include <linux/netdevice.h>
+-#include <linux/if_addr.h>
+-#include <linux/if_arp.h>
+-#include <linux/if_arcnet.h>
+-#include <linux/if_infiniband.h>
+-#include <linux/route.h>
+-#include <linux/inetdevice.h>
+-#include <linux/init.h>
+-#ifdef CONFIG_SYSCTL
+-#include <linux/sysctl.h>
+-#endif
+-#include <linux/capability.h>
+-#include <linux/delay.h>
+-#include <linux/notifier.h>
+-#include <linux/string.h>
+-
+-#include <net/sock.h>
+-#include <net/snmp.h>
+-
+-#include <net/ipv6.h>
+-#include <net/protocol.h>
+-#include <net/ndisc.h>
+-#include <net/ip6_route.h>
+-#include <net/addrconf.h>
+-#include <net/tcp.h>
+-#include <net/ip.h>
+-#include <net/netlink.h>
+-#include <net/pkt_sched.h>
+-#include <linux/if_tunnel.h>
+-#include <linux/rtnetlink.h>
+-
+-#ifdef CONFIG_IPV6_PRIVACY
+-#include <linux/random.h>
+-#endif
+-
+-#include <asm/uaccess.h>
+-#include <asm/unaligned.h>
+-
+-#include <linux/proc_fs.h>
+-#include <linux/seq_file.h>
+-
+-/* Set to 3 to get tracing... */
+-#define ACONF_DEBUG 2
+-
+-#if ACONF_DEBUG >= 3
+-#define ADBG(x) printk x
+-#else
+-#define ADBG(x)
+-#endif
+-
+-#define INFINITY_LIFE_TIME 0xFFFFFFFF
+-#define TIME_DELTA(a,b) ((unsigned long)((long)(a) - (long)(b)))
+-
+-#ifdef CONFIG_SYSCTL
+-static void addrconf_sysctl_register(struct inet6_dev *idev, struct ipv6_devconf *p);
+-static void addrconf_sysctl_unregister(struct ipv6_devconf *p);
+-#endif
+-
+-#ifdef CONFIG_IPV6_PRIVACY
+-static int __ipv6_regen_rndid(struct inet6_dev *idev);
+-static int __ipv6_try_regen_rndid(struct inet6_dev *idev, struct in6_addr *tmpaddr);
+-static void ipv6_regen_rndid(unsigned long data);
+-
+-static int desync_factor = MAX_DESYNC_FACTOR * HZ;
+-#endif
+-
+-static int ipv6_count_addresses(struct inet6_dev *idev);
+-
+-/*
+- * Configured unicast address hash table
+- */
+-static struct inet6_ifaddr *inet6_addr_lst[IN6_ADDR_HSIZE];
+-static DEFINE_RWLOCK(addrconf_hash_lock);
+-
+-static void addrconf_verify(unsigned long);
+-
+-static DEFINE_TIMER(addr_chk_timer, addrconf_verify, 0, 0);
+-static DEFINE_SPINLOCK(addrconf_verify_lock);
+-
+-static void addrconf_join_anycast(struct inet6_ifaddr *ifp);
+-static void addrconf_leave_anycast(struct inet6_ifaddr *ifp);
+-
+-static int addrconf_ifdown(struct net_device *dev, int how);
+-
+-static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags);
+-static void addrconf_dad_timer(unsigned long data);
+-static void addrconf_dad_completed(struct inet6_ifaddr *ifp);
+-static void addrconf_dad_run(struct inet6_dev *idev);
+-static void addrconf_rs_timer(unsigned long data);
+-static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifa);
+-static void ipv6_ifa_notify(int event, struct inet6_ifaddr *ifa);
+-
+-static void inet6_prefix_notify(int event, struct inet6_dev *idev,
+- struct prefix_info *pinfo);
+-static int ipv6_chk_same_addr(const struct in6_addr *addr, struct net_device *dev);
+-
+-static ATOMIC_NOTIFIER_HEAD(inet6addr_chain);
+-
+-struct ipv6_devconf ipv6_devconf __read_mostly = {
+- .forwarding = 0,
+- .hop_limit = IPV6_DEFAULT_HOPLIMIT,
+- .mtu6 = IPV6_MIN_MTU,
+- .accept_ra = 1,
+- .accept_redirects = 1,
+- .autoconf = 1,
+- .force_mld_version = 0,
+- .dad_transmits = 1,
+- .rtr_solicits = MAX_RTR_SOLICITATIONS,
+- .rtr_solicit_interval = RTR_SOLICITATION_INTERVAL,
+- .rtr_solicit_delay = MAX_RTR_SOLICITATION_DELAY,
+-#ifdef CONFIG_IPV6_PRIVACY
+- .use_tempaddr = 0,
+- .temp_valid_lft = TEMP_VALID_LIFETIME,
+- .temp_prefered_lft = TEMP_PREFERRED_LIFETIME,
+- .regen_max_retry = REGEN_MAX_RETRY,
+- .max_desync_factor = MAX_DESYNC_FACTOR,
+-#endif
+- .max_addresses = IPV6_MAX_ADDRESSES,
+- .accept_ra_defrtr = 1,
+- .accept_ra_pinfo = 1,
+-#ifdef CONFIG_IPV6_ROUTER_PREF
+- .accept_ra_rtr_pref = 1,
+- .rtr_probe_interval = 60 * HZ,
+-#ifdef CONFIG_IPV6_ROUTE_INFO
+- .accept_ra_rt_info_max_plen = 0,
+-#endif
+-#endif
+- .proxy_ndp = 0,
+- .accept_source_route = 0, /* we do not accept RH0 by default. */
+-};
+-
+-static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
+- .forwarding = 0,
+- .hop_limit = IPV6_DEFAULT_HOPLIMIT,
+- .mtu6 = IPV6_MIN_MTU,
+- .accept_ra = 1,
+- .accept_redirects = 1,
+- .autoconf = 1,
+- .dad_transmits = 1,
+- .rtr_solicits = MAX_RTR_SOLICITATIONS,
+- .rtr_solicit_interval = RTR_SOLICITATION_INTERVAL,
+- .rtr_solicit_delay = MAX_RTR_SOLICITATION_DELAY,
+-#ifdef CONFIG_IPV6_PRIVACY
+- .use_tempaddr = 0,
+- .temp_valid_lft = TEMP_VALID_LIFETIME,
+- .temp_prefered_lft = TEMP_PREFERRED_LIFETIME,
+- .regen_max_retry = REGEN_MAX_RETRY,
+- .max_desync_factor = MAX_DESYNC_FACTOR,
+-#endif
+- .max_addresses = IPV6_MAX_ADDRESSES,
+- .accept_ra_defrtr = 1,
+- .accept_ra_pinfo = 1,
+-#ifdef CONFIG_IPV6_ROUTER_PREF
+- .accept_ra_rtr_pref = 1,
+- .rtr_probe_interval = 60 * HZ,
+-#ifdef CONFIG_IPV6_ROUTE_INFO
+- .accept_ra_rt_info_max_plen = 0,
+-#endif
+-#endif
+- .proxy_ndp = 0,
+- .accept_source_route = 0, /* we do not accept RH0 by default. */
+-};
+-
+-/* IPv6 Wildcard Address and Loopback Address defined by RFC2553 */
+-const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT;
+-const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT;
+-
+-/* Check if a valid qdisc is available */
+-static inline int addrconf_qdisc_ok(struct net_device *dev)
+-{
+- return (dev->qdisc != &noop_qdisc);
+-}
+-
+-static void addrconf_del_timer(struct inet6_ifaddr *ifp)
+-{
+- if (del_timer(&ifp->timer))
+- __in6_ifa_put(ifp);
+-}
+-
+-enum addrconf_timer_t
+-{
+- AC_NONE,
+- AC_DAD,
+- AC_RS,
+-};
+-
+-static void addrconf_mod_timer(struct inet6_ifaddr *ifp,
+- enum addrconf_timer_t what,
+- unsigned long when)
+-{
+- if (!del_timer(&ifp->timer))
+- in6_ifa_hold(ifp);
+-
+- switch (what) {
+- case AC_DAD:
+- ifp->timer.function = addrconf_dad_timer;
+- break;
+- case AC_RS:
+- ifp->timer.function = addrconf_rs_timer;
+- break;
+- default:;
+- }
+- ifp->timer.expires = jiffies + when;
+- add_timer(&ifp->timer);
+-}
+-
+-static int snmp6_alloc_dev(struct inet6_dev *idev)
+-{
+- int err = -ENOMEM;
+-
+- if (!idev || !idev->dev)
+- return -EINVAL;
+-
+- if (snmp_mib_init((void **)idev->stats.ipv6,
+- sizeof(struct ipstats_mib),
+- __alignof__(struct ipstats_mib)) < 0)
+- goto err_ip;
+- if (snmp_mib_init((void **)idev->stats.icmpv6,
+- sizeof(struct icmpv6_mib),
+- __alignof__(struct icmpv6_mib)) < 0)
+- goto err_icmp;
+-
+- return 0;
+-
+-err_icmp:
+- snmp_mib_free((void **)idev->stats.ipv6);
+-err_ip:
+- return err;
+-}
+-
+-static int snmp6_free_dev(struct inet6_dev *idev)
+-{
+- snmp_mib_free((void **)idev->stats.icmpv6);
+- snmp_mib_free((void **)idev->stats.ipv6);
+- return 0;
+-}
+-
+-/* Nobody refers to this device, we may destroy it. */
+-
+-static void in6_dev_finish_destroy_rcu(struct rcu_head *head)
+-{
+- struct inet6_dev *idev = container_of(head, struct inet6_dev, rcu);
+- kfree(idev);
+-}
+-
+-void in6_dev_finish_destroy(struct inet6_dev *idev)
+-{
+- struct net_device *dev = idev->dev;
+- BUG_TRAP(idev->addr_list==NULL);
+- BUG_TRAP(idev->mc_list==NULL);
+-#ifdef NET_REFCNT_DEBUG
+- printk(KERN_DEBUG "in6_dev_finish_destroy: %s\n", dev ? dev->name : "NIL");
+-#endif
+- dev_put(dev);
+- if (!idev->dead) {
+- printk("Freeing alive inet6 device %p\n", idev);
+- return;
+- }
+- snmp6_free_dev(idev);
+- call_rcu(&idev->rcu, in6_dev_finish_destroy_rcu);
+-}
+-
+-EXPORT_SYMBOL(in6_dev_finish_destroy);
+-
+-static struct inet6_dev * ipv6_add_dev(struct net_device *dev)
+-{
+- struct inet6_dev *ndev;
+- struct in6_addr maddr;
+-
+- ASSERT_RTNL();
+-
+- if (dev->mtu < IPV6_MIN_MTU)
+- return NULL;
+-
+- ndev = kzalloc(sizeof(struct inet6_dev), GFP_KERNEL);
+-
+- if (ndev == NULL)
+- return NULL;
+-
+- rwlock_init(&ndev->lock);
+- ndev->dev = dev;
+- memcpy(&ndev->cnf, &ipv6_devconf_dflt, sizeof(ndev->cnf));
+- ndev->cnf.mtu6 = dev->mtu;
+- ndev->cnf.sysctl = NULL;
+- ndev->nd_parms = neigh_parms_alloc(dev, &nd_tbl);
+- if (ndev->nd_parms == NULL) {
+- kfree(ndev);
+- return NULL;
+- }
+- /* We refer to the device */
+- dev_hold(dev);
+-
+- if (snmp6_alloc_dev(ndev) < 0) {
+- ADBG((KERN_WARNING
+- "%s(): cannot allocate memory for statistics; dev=%s.\n",
+- __FUNCTION__, dev->name));
+- neigh_parms_release(&nd_tbl, ndev->nd_parms);
+- ndev->dead = 1;
+- in6_dev_finish_destroy(ndev);
+- return NULL;
+- }
+-
+- if (snmp6_register_dev(ndev) < 0) {
+- ADBG((KERN_WARNING
+- "%s(): cannot create /proc/net/dev_snmp6/%s\n",
+- __FUNCTION__, dev->name));
+- neigh_parms_release(&nd_tbl, ndev->nd_parms);
+- ndev->dead = 1;
+- in6_dev_finish_destroy(ndev);
+- return NULL;
+- }
+-
+- /* One reference from device. We must do this before
+- * we invoke __ipv6_regen_rndid().
+- */
+- in6_dev_hold(ndev);
+-
+-#ifdef CONFIG_IPV6_PRIVACY
+- init_timer(&ndev->regen_timer);
+- ndev->regen_timer.function = ipv6_regen_rndid;
+- ndev->regen_timer.data = (unsigned long) ndev;
+- if ((dev->flags&IFF_LOOPBACK) ||
+- dev->type == ARPHRD_TUNNEL ||
+-#if defined(CONFIG_IPV6_SIT) || defined(CONFIG_IPV6_SIT_MODULE)
+- dev->type == ARPHRD_SIT ||
+-#endif
+- dev->type == ARPHRD_NONE) {
+- printk(KERN_INFO
+- "%s: Disabled Privacy Extensions\n",
+- dev->name);
+- ndev->cnf.use_tempaddr = -1;
+- } else {
+- in6_dev_hold(ndev);
+- ipv6_regen_rndid((unsigned long) ndev);
+- }
+-#endif
+-
+- if (netif_running(dev) && addrconf_qdisc_ok(dev))
+- ndev->if_flags |= IF_READY;
+-
+- ipv6_mc_init_dev(ndev);
+- ndev->tstamp = jiffies;
+-#ifdef CONFIG_SYSCTL
+- neigh_sysctl_register(dev, ndev->nd_parms, NET_IPV6,
+- NET_IPV6_NEIGH, "ipv6",
+- &ndisc_ifinfo_sysctl_change,
+- NULL);
+- addrconf_sysctl_register(ndev, &ndev->cnf);
+-#endif
+- /* protected by rtnl_lock */
+- rcu_assign_pointer(dev->ip6_ptr, ndev);
+-
+- /* Join all-node multicast group */
+- ipv6_addr_all_nodes(&maddr);
+- ipv6_dev_mc_inc(dev, &maddr);
+-
+- return ndev;
+-}
+-
+-static struct inet6_dev * ipv6_find_idev(struct net_device *dev)
+-{
+- struct inet6_dev *idev;
+-
+- ASSERT_RTNL();
+-
+- if ((idev = __in6_dev_get(dev)) == NULL) {
+- if ((idev = ipv6_add_dev(dev)) == NULL)
+- return NULL;
+- }
+-
+- if (dev->flags&IFF_UP)
+- ipv6_mc_up(idev);
+- return idev;
+-}
+-
+-#ifdef CONFIG_SYSCTL
+-static void dev_forward_change(struct inet6_dev *idev)
+-{
+- struct net_device *dev;
+- struct inet6_ifaddr *ifa;
+- struct in6_addr addr;
+-
+- if (!idev)
+- return;
+- dev = idev->dev;
+- if (dev && (dev->flags & IFF_MULTICAST)) {
+- ipv6_addr_all_routers(&addr);
+-
+- if (idev->cnf.forwarding)
+- ipv6_dev_mc_inc(dev, &addr);
+- else
+- ipv6_dev_mc_dec(dev, &addr);
+- }
+- for (ifa=idev->addr_list; ifa; ifa=ifa->if_next) {
+- if (ifa->flags&IFA_F_TENTATIVE)
+- continue;
+- if (idev->cnf.forwarding)
+- addrconf_join_anycast(ifa);
+- else
+- addrconf_leave_anycast(ifa);
+- }
+-}
+-
+-
+-static void addrconf_forward_change(void)
+-{
+- struct net_device *dev;
+- struct inet6_dev *idev;
+-
+- read_lock(&dev_base_lock);
+- for_each_netdev(dev) {
+- rcu_read_lock();
+- idev = __in6_dev_get(dev);
+- if (idev) {
+- int changed = (!idev->cnf.forwarding) ^ (!ipv6_devconf.forwarding);
+- idev->cnf.forwarding = ipv6_devconf.forwarding;
+- if (changed)
+- dev_forward_change(idev);
+- }
+- rcu_read_unlock();
+- }
+- read_unlock(&dev_base_lock);
+-}
+-#endif
+-
+-/* Nobody refers to this ifaddr, destroy it */
+-
+-void inet6_ifa_finish_destroy(struct inet6_ifaddr *ifp)
+-{
+- BUG_TRAP(ifp->if_next==NULL);
+- BUG_TRAP(ifp->lst_next==NULL);
+-#ifdef NET_REFCNT_DEBUG
+- printk(KERN_DEBUG "inet6_ifa_finish_destroy\n");
+-#endif
+-
+- in6_dev_put(ifp->idev);
+-
+- if (del_timer(&ifp->timer))
+- printk("Timer is still running, when freeing ifa=%p\n", ifp);
+-
+- if (!ifp->dead) {
+- printk("Freeing alive inet6 address %p\n", ifp);
+- return;
+- }
+- dst_release(&ifp->rt->u.dst);
+-
+- kfree(ifp);
+-}
+-
+-static void
+-ipv6_link_dev_addr(struct inet6_dev *idev, struct inet6_ifaddr *ifp)
+-{
+- struct inet6_ifaddr *ifa, **ifap;
+- int ifp_scope = ipv6_addr_src_scope(&ifp->addr);
+-
+- /*
+- * Each device address list is sorted in order of scope -
+- * global before linklocal.
+- */
+- for (ifap = &idev->addr_list; (ifa = *ifap) != NULL;
+- ifap = &ifa->if_next) {
+- if (ifp_scope >= ipv6_addr_src_scope(&ifa->addr))
+- break;
+- }
+-
+- ifp->if_next = *ifap;
+- *ifap = ifp;
+-}
+-
+-/* On success it returns ifp with increased reference count */
+-
+-static struct inet6_ifaddr *
+-ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, int pfxlen,
+- int scope, u32 flags)
+-{
+- struct inet6_ifaddr *ifa = NULL;
+- struct rt6_info *rt;
+- int hash;
+- int err = 0;
+-
+- rcu_read_lock_bh();
+- if (idev->dead) {
+- err = -ENODEV; /*XXX*/
+- goto out2;
+- }
+-
+- write_lock(&addrconf_hash_lock);
+-
+- /* Ignore adding duplicate addresses on an interface */
+- if (ipv6_chk_same_addr(addr, idev->dev)) {
+- ADBG(("ipv6_add_addr: already assigned\n"));
+- err = -EEXIST;
+- goto out;
+- }
+-
+- ifa = kzalloc(sizeof(struct inet6_ifaddr), GFP_ATOMIC);
+-
+- if (ifa == NULL) {
+- ADBG(("ipv6_add_addr: malloc failed\n"));
+- err = -ENOBUFS;
+- goto out;
+- }
+-
+- rt = addrconf_dst_alloc(idev, addr, 0);
+- if (IS_ERR(rt)) {
+- err = PTR_ERR(rt);
+- goto out;
+- }
+-
+- ipv6_addr_copy(&ifa->addr, addr);
+-
+- spin_lock_init(&ifa->lock);
+- init_timer(&ifa->timer);
+- ifa->timer.data = (unsigned long) ifa;
+- ifa->scope = scope;
+- ifa->prefix_len = pfxlen;
+- ifa->flags = flags | IFA_F_TENTATIVE;
+- ifa->cstamp = ifa->tstamp = jiffies;
+-
+- ifa->rt = rt;
+-
+- /*
+- * part one of RFC 4429, section 3.3
+- * We should not configure an address as
+- * optimistic if we do not yet know the link
+- * layer address of our nexhop router
+- */
+-
+- if (rt->rt6i_nexthop == NULL)
+- ifa->flags &= ~IFA_F_OPTIMISTIC;
+-
+- ifa->idev = idev;
+- in6_dev_hold(idev);
+- /* For caller */
+- in6_ifa_hold(ifa);
+-
+- /* Add to big hash table */
+- hash = ipv6_addr_hash(addr);
+-
+- ifa->lst_next = inet6_addr_lst[hash];
+- inet6_addr_lst[hash] = ifa;
+- in6_ifa_hold(ifa);
+- write_unlock(&addrconf_hash_lock);
+-
+- write_lock(&idev->lock);
+- /* Add to inet6_dev unicast addr list. */
+- ipv6_link_dev_addr(idev, ifa);
+-
+-#ifdef CONFIG_IPV6_PRIVACY
+- if (ifa->flags&IFA_F_TEMPORARY) {
+- ifa->tmp_next = idev->tempaddr_list;
+- idev->tempaddr_list = ifa;
+- in6_ifa_hold(ifa);
+- }
+-#endif
+-
+- in6_ifa_hold(ifa);
+- write_unlock(&idev->lock);
+-out2:
+- rcu_read_unlock_bh();
+-
+- if (likely(err == 0))
+- atomic_notifier_call_chain(&inet6addr_chain, NETDEV_UP, ifa);
+- else {
+- kfree(ifa);
+- ifa = ERR_PTR(err);
+- }
+-
+- return ifa;
+-out:
+- write_unlock(&addrconf_hash_lock);
+- goto out2;
+-}
+-
+-/* This function wants to get referenced ifp and releases it before return */
+-
+-static void ipv6_del_addr(struct inet6_ifaddr *ifp)
+-{
+- struct inet6_ifaddr *ifa, **ifap;
+- struct inet6_dev *idev = ifp->idev;
+- int hash;
+- int deleted = 0, onlink = 0;
+- unsigned long expires = jiffies;
+-
+- hash = ipv6_addr_hash(&ifp->addr);
+-
+- ifp->dead = 1;
+-
+- write_lock_bh(&addrconf_hash_lock);
+- for (ifap = &inet6_addr_lst[hash]; (ifa=*ifap) != NULL;
+- ifap = &ifa->lst_next) {
+- if (ifa == ifp) {
+- *ifap = ifa->lst_next;
+- __in6_ifa_put(ifp);
+- ifa->lst_next = NULL;
+- break;
+- }
+- }
+- write_unlock_bh(&addrconf_hash_lock);
+-
+- write_lock_bh(&idev->lock);
+-#ifdef CONFIG_IPV6_PRIVACY
+- if (ifp->flags&IFA_F_TEMPORARY) {
+- for (ifap = &idev->tempaddr_list; (ifa=*ifap) != NULL;
+- ifap = &ifa->tmp_next) {
+- if (ifa == ifp) {
+- *ifap = ifa->tmp_next;
+- if (ifp->ifpub) {
+- in6_ifa_put(ifp->ifpub);
+- ifp->ifpub = NULL;
+- }
+- __in6_ifa_put(ifp);
+- ifa->tmp_next = NULL;
+- break;
+- }
+- }
+- }
+-#endif
+-
+- for (ifap = &idev->addr_list; (ifa=*ifap) != NULL;) {
+- if (ifa == ifp) {
+- *ifap = ifa->if_next;
+- __in6_ifa_put(ifp);
+- ifa->if_next = NULL;
+- if (!(ifp->flags & IFA_F_PERMANENT) || onlink > 0)
+- break;
+- deleted = 1;
+- continue;
+- } else if (ifp->flags & IFA_F_PERMANENT) {
+- if (ipv6_prefix_equal(&ifa->addr, &ifp->addr,
+- ifp->prefix_len)) {
+- if (ifa->flags & IFA_F_PERMANENT) {
+- onlink = 1;
+- if (deleted)
+- break;
+- } else {
+- unsigned long lifetime;
+-
+- if (!onlink)
+- onlink = -1;
+-
+- spin_lock(&ifa->lock);
+- lifetime = min_t(unsigned long,
+- ifa->valid_lft, 0x7fffffffUL/HZ);
+- if (time_before(expires,
+- ifa->tstamp + lifetime * HZ))
+- expires = ifa->tstamp + lifetime * HZ;
+- spin_unlock(&ifa->lock);
+- }
+- }
+- }
+- ifap = &ifa->if_next;
+- }
+- write_unlock_bh(&idev->lock);
+-
+- ipv6_ifa_notify(RTM_DELADDR, ifp);
+-
+- atomic_notifier_call_chain(&inet6addr_chain, NETDEV_DOWN, ifp);
+-
+- addrconf_del_timer(ifp);
+-
+- /*
+- * Purge or update corresponding prefix
+- *
+- * 1) we don't purge prefix here if address was not permanent.
+- * prefix is managed by its own lifetime.
+- * 2) if there're no addresses, delete prefix.
+- * 3) if there're still other permanent address(es),
+- * corresponding prefix is still permanent.
+- * 4) otherwise, update prefix lifetime to the
+- * longest valid lifetime among the corresponding
+- * addresses on the device.
+- * Note: subsequent RA will update lifetime.
+- *
+- * --yoshfuji
+- */
+- if ((ifp->flags & IFA_F_PERMANENT) && onlink < 1) {
+- struct in6_addr prefix;
+- struct rt6_info *rt;
+-
+- ipv6_addr_prefix(&prefix, &ifp->addr, ifp->prefix_len);
+- rt = rt6_lookup(&prefix, NULL, ifp->idev->dev->ifindex, 1);
+-
+- if (rt && ((rt->rt6i_flags & (RTF_GATEWAY | RTF_DEFAULT)) == 0)) {
+- if (onlink == 0) {
+- ip6_del_rt(rt);
+- rt = NULL;
+- } else if (!(rt->rt6i_flags & RTF_EXPIRES)) {
+- rt->rt6i_expires = expires;
+- rt->rt6i_flags |= RTF_EXPIRES;
+- }
+- }
+- dst_release(&rt->u.dst);
+- }
+-
+- in6_ifa_put(ifp);
+-}
+-
+-#ifdef CONFIG_IPV6_PRIVACY
+-static int ipv6_create_tempaddr(struct inet6_ifaddr *ifp, struct inet6_ifaddr *ift)
+-{
+- struct inet6_dev *idev = ifp->idev;
+- struct in6_addr addr, *tmpaddr;
+- unsigned long tmp_prefered_lft, tmp_valid_lft, tmp_cstamp, tmp_tstamp;
+- int tmp_plen;
+- int ret = 0;
+- int max_addresses;
+- u32 addr_flags;
+-
+- write_lock(&idev->lock);
+- if (ift) {
+- spin_lock_bh(&ift->lock);
+- memcpy(&addr.s6_addr[8], &ift->addr.s6_addr[8], 8);
+- spin_unlock_bh(&ift->lock);
+- tmpaddr = &addr;
+- } else {
+- tmpaddr = NULL;
+- }
+-retry:
+- in6_dev_hold(idev);
+- if (idev->cnf.use_tempaddr <= 0) {
+- write_unlock(&idev->lock);
+- printk(KERN_INFO
+- "ipv6_create_tempaddr(): use_tempaddr is disabled.\n");
+- in6_dev_put(idev);
+- ret = -1;
+- goto out;
+- }
+- spin_lock_bh(&ifp->lock);
+- if (ifp->regen_count++ >= idev->cnf.regen_max_retry) {
+- idev->cnf.use_tempaddr = -1; /*XXX*/
+- spin_unlock_bh(&ifp->lock);
+- write_unlock(&idev->lock);
+- printk(KERN_WARNING
+- "ipv6_create_tempaddr(): regeneration time exceeded. disabled temporary address support.\n");
+- in6_dev_put(idev);
+- ret = -1;
+- goto out;
+- }
+- in6_ifa_hold(ifp);
+- memcpy(addr.s6_addr, ifp->addr.s6_addr, 8);
+- if (__ipv6_try_regen_rndid(idev, tmpaddr) < 0) {
+- spin_unlock_bh(&ifp->lock);
+- write_unlock(&idev->lock);
+- printk(KERN_WARNING
+- "ipv6_create_tempaddr(): regeneration of randomized interface id failed.\n");
+- in6_ifa_put(ifp);
+- in6_dev_put(idev);
+- ret = -1;
+- goto out;
+- }
+- memcpy(&addr.s6_addr[8], idev->rndid, 8);
+- tmp_valid_lft = min_t(__u32,
+- ifp->valid_lft,
+- idev->cnf.temp_valid_lft);
+- tmp_prefered_lft = min_t(__u32,
+- ifp->prefered_lft,
+- idev->cnf.temp_prefered_lft - desync_factor / HZ);
+- tmp_plen = ifp->prefix_len;
+- max_addresses = idev->cnf.max_addresses;
+- tmp_cstamp = ifp->cstamp;
+- tmp_tstamp = ifp->tstamp;
+- spin_unlock_bh(&ifp->lock);
+-
+- write_unlock(&idev->lock);
+-
+- addr_flags = IFA_F_TEMPORARY;
+- /* set in addrconf_prefix_rcv() */
+- if (ifp->flags & IFA_F_OPTIMISTIC)
+- addr_flags |= IFA_F_OPTIMISTIC;
+-
+- ift = !max_addresses ||
+- ipv6_count_addresses(idev) < max_addresses ?
+- ipv6_add_addr(idev, &addr, tmp_plen,
+- ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK,
+- addr_flags) : NULL;
+- if (!ift || IS_ERR(ift)) {
+- in6_ifa_put(ifp);
+- in6_dev_put(idev);
+- printk(KERN_INFO
+- "ipv6_create_tempaddr(): retry temporary address regeneration.\n");
+- tmpaddr = &addr;
+- write_lock(&idev->lock);
+- goto retry;
+- }
+-
+- spin_lock_bh(&ift->lock);
+- ift->ifpub = ifp;
+- ift->valid_lft = tmp_valid_lft;
+- ift->prefered_lft = tmp_prefered_lft;
+- ift->cstamp = tmp_cstamp;
+- ift->tstamp = tmp_tstamp;
+- spin_unlock_bh(&ift->lock);
+-
+- addrconf_dad_start(ift, 0);
+- in6_ifa_put(ift);
+- in6_dev_put(idev);
+-out:
+- return ret;
+-}
+-#endif
+-
+-/*
+- * Choose an appropriate source address (RFC3484)
+- */
+-struct ipv6_saddr_score {
+- int addr_type;
+- unsigned int attrs;
+- int matchlen;
+- int scope;
+- unsigned int rule;
+-};
+-
+-#define IPV6_SADDR_SCORE_LOCAL 0x0001
+-#define IPV6_SADDR_SCORE_PREFERRED 0x0004
+-#define IPV6_SADDR_SCORE_HOA 0x0008
+-#define IPV6_SADDR_SCORE_OIF 0x0010
+-#define IPV6_SADDR_SCORE_LABEL 0x0020
+-#define IPV6_SADDR_SCORE_PRIVACY 0x0040
+-
+-static inline int ipv6_saddr_preferred(int type)
+-{
+- if (type & (IPV6_ADDR_MAPPED|IPV6_ADDR_COMPATv4|
+- IPV6_ADDR_LOOPBACK|IPV6_ADDR_RESERVED))
+- return 1;
+- return 0;
+-}
+-
+-/* static matching label */
+-static inline int ipv6_saddr_label(const struct in6_addr *addr, int type)
+-{
+- /*
+- * prefix (longest match) label
+- * -----------------------------
+- * ::1/128 0
+- * ::/0 1
+- * 2002::/16 2
+- * ::/96 3
+- * ::ffff:0:0/96 4
+- * fc00::/7 5
+- * 2001::/32 6
+- */
+- if (type & IPV6_ADDR_LOOPBACK)
+- return 0;
+- else if (type & IPV6_ADDR_COMPATv4)
+- return 3;
+- else if (type & IPV6_ADDR_MAPPED)
+- return 4;
+- else if (addr->s6_addr32[0] == htonl(0x20010000))
+- return 6;
+- else if (addr->s6_addr16[0] == htons(0x2002))
+- return 2;
+- else if ((addr->s6_addr[0] & 0xfe) == 0xfc)
+- return 5;
+- return 1;
+-}
+-
+-int ipv6_dev_get_saddr(struct net_device *daddr_dev,
+- struct in6_addr *daddr, struct in6_addr *saddr)
+-{
+- struct ipv6_saddr_score hiscore;
+- struct inet6_ifaddr *ifa_result = NULL;
+- int daddr_type = __ipv6_addr_type(daddr);
+- int daddr_scope = __ipv6_addr_src_scope(daddr_type);
+- u32 daddr_label = ipv6_saddr_label(daddr, daddr_type);
+- struct net_device *dev;
+-
+- memset(&hiscore, 0, sizeof(hiscore));
+-
+- read_lock(&dev_base_lock);
+- rcu_read_lock();
+-
+- for_each_netdev(dev) {
+- struct inet6_dev *idev;
+- struct inet6_ifaddr *ifa;
+-
+- /* Rule 0: Candidate Source Address (section 4)
+- * - multicast and link-local destination address,
+- * the set of candidate source address MUST only
+- * include addresses assigned to interfaces
+- * belonging to the same link as the outgoing
+- * interface.
+- * (- For site-local destination addresses, the
+- * set of candidate source addresses MUST only
+- * include addresses assigned to interfaces
+- * belonging to the same site as the outgoing
+- * interface.)
+- */
+- if ((daddr_type & IPV6_ADDR_MULTICAST ||
+- daddr_scope <= IPV6_ADDR_SCOPE_LINKLOCAL) &&
+- daddr_dev && dev != daddr_dev)
+- continue;
+-
+- idev = __in6_dev_get(dev);
+- if (!idev)
+- continue;
+-
+- read_lock_bh(&idev->lock);
+- for (ifa = idev->addr_list; ifa; ifa = ifa->if_next) {
+- struct ipv6_saddr_score score;
+-
+- score.addr_type = __ipv6_addr_type(&ifa->addr);
+-
+- /* Rule 0:
+- * - Tentative Address (RFC2462 section 5.4)
+- * - A tentative address is not considered
+- * "assigned to an interface" in the traditional
+- * sense, unless it is also flagged as optimistic.
+- * - Candidate Source Address (section 4)
+- * - In any case, anycast addresses, multicast
+- * addresses, and the unspecified address MUST
+- * NOT be included in a candidate set.
+- */
+- if ((ifa->flags & IFA_F_TENTATIVE) &&
+- (!(ifa->flags & IFA_F_OPTIMISTIC)))
+- continue;
+- if (unlikely(score.addr_type == IPV6_ADDR_ANY ||
+- score.addr_type & IPV6_ADDR_MULTICAST)) {
+- LIMIT_NETDEBUG(KERN_DEBUG
+- "ADDRCONF: unspecified / multicast address"
+- "assigned as unicast address on %s",
+- dev->name);
+- continue;
+- }
+-
+- score.attrs = 0;
+- score.matchlen = 0;
+- score.scope = 0;
+- score.rule = 0;
+-
+- if (ifa_result == NULL) {
+- /* record it if the first available entry */
+- goto record_it;
+- }
+-
+- /* Rule 1: Prefer same address */
+- if (hiscore.rule < 1) {
+- if (ipv6_addr_equal(&ifa_result->addr, daddr))
+- hiscore.attrs |= IPV6_SADDR_SCORE_LOCAL;
+- hiscore.rule++;
+- }
+- if (ipv6_addr_equal(&ifa->addr, daddr)) {
+- score.attrs |= IPV6_SADDR_SCORE_LOCAL;
+- if (!(hiscore.attrs & IPV6_SADDR_SCORE_LOCAL)) {
+- score.rule = 1;
+- goto record_it;
+- }
+- } else {
+- if (hiscore.attrs & IPV6_SADDR_SCORE_LOCAL)
+- continue;
+- }
+-
+- /* Rule 2: Prefer appropriate scope */
+- if (hiscore.rule < 2) {
+- hiscore.scope = __ipv6_addr_src_scope(hiscore.addr_type);
+- hiscore.rule++;
+- }
+- score.scope = __ipv6_addr_src_scope(score.addr_type);
+- if (hiscore.scope < score.scope) {
+- if (hiscore.scope < daddr_scope) {
+- score.rule = 2;
+- goto record_it;
+- } else
+- continue;
+- } else if (score.scope < hiscore.scope) {
+- if (score.scope < daddr_scope)
+- break; /* addresses sorted by scope */
+- else {
+- score.rule = 2;
+- goto record_it;
+- }
+- }
+-
+- /* Rule 3: Avoid deprecated and optimistic addresses */
+- if (hiscore.rule < 3) {
+- if (ipv6_saddr_preferred(hiscore.addr_type) ||
+- (((ifa_result->flags &
+- (IFA_F_DEPRECATED|IFA_F_OPTIMISTIC)) == 0)))
+- hiscore.attrs |= IPV6_SADDR_SCORE_PREFERRED;
+- hiscore.rule++;
+- }
+- if (ipv6_saddr_preferred(score.addr_type) ||
+- (((ifa->flags &
+- (IFA_F_DEPRECATED|IFA_F_OPTIMISTIC)) == 0))) {
+- score.attrs |= IPV6_SADDR_SCORE_PREFERRED;
+- if (!(hiscore.attrs & IPV6_SADDR_SCORE_PREFERRED)) {
+- score.rule = 3;
+- goto record_it;
+- }
+- } else {
+- if (hiscore.attrs & IPV6_SADDR_SCORE_PREFERRED)
+- continue;
+- }
+-
+- /* Rule 4: Prefer home address */
+-#ifdef CONFIG_IPV6_MIP6
+- if (hiscore.rule < 4) {
+- if (ifa_result->flags & IFA_F_HOMEADDRESS)
+- hiscore.attrs |= IPV6_SADDR_SCORE_HOA;
+- hiscore.rule++;
+- }
+- if (ifa->flags & IFA_F_HOMEADDRESS) {
+- score.attrs |= IPV6_SADDR_SCORE_HOA;
+- if (!(ifa_result->flags & IFA_F_HOMEADDRESS)) {
+- score.rule = 4;
+- goto record_it;
+- }
+- } else {
+- if (hiscore.attrs & IPV6_SADDR_SCORE_HOA)
+- continue;
+- }
+-#else
+- if (hiscore.rule < 4)
+- hiscore.rule++;
+-#endif
+-
+- /* Rule 5: Prefer outgoing interface */
+- if (hiscore.rule < 5) {
+- if (daddr_dev == NULL ||
+- daddr_dev == ifa_result->idev->dev)
+- hiscore.attrs |= IPV6_SADDR_SCORE_OIF;
+- hiscore.rule++;
+- }
+- if (daddr_dev == NULL ||
+- daddr_dev == ifa->idev->dev) {
+- score.attrs |= IPV6_SADDR_SCORE_OIF;
+- if (!(hiscore.attrs & IPV6_SADDR_SCORE_OIF)) {
+- score.rule = 5;
+- goto record_it;
+- }
+- } else {
+- if (hiscore.attrs & IPV6_SADDR_SCORE_OIF)
+- continue;
+- }
+-
+- /* Rule 6: Prefer matching label */
+- if (hiscore.rule < 6) {
+- if (ipv6_saddr_label(&ifa_result->addr, hiscore.addr_type) == daddr_label)
+- hiscore.attrs |= IPV6_SADDR_SCORE_LABEL;
+- hiscore.rule++;
+- }
+- if (ipv6_saddr_label(&ifa->addr, score.addr_type) == daddr_label) {
+- score.attrs |= IPV6_SADDR_SCORE_LABEL;
+- if (!(hiscore.attrs & IPV6_SADDR_SCORE_LABEL)) {
+- score.rule = 6;
+- goto record_it;
+- }
+- } else {
+- if (hiscore.attrs & IPV6_SADDR_SCORE_LABEL)
+- continue;
+- }
+-
+-#ifdef CONFIG_IPV6_PRIVACY
+- /* Rule 7: Prefer public address
+- * Note: prefer temprary address if use_tempaddr >= 2
+- */
+- if (hiscore.rule < 7) {
+- if ((!(ifa_result->flags & IFA_F_TEMPORARY)) ^
+- (ifa_result->idev->cnf.use_tempaddr >= 2))
+- hiscore.attrs |= IPV6_SADDR_SCORE_PRIVACY;
+- hiscore.rule++;
+- }
+- if ((!(ifa->flags & IFA_F_TEMPORARY)) ^
+- (ifa->idev->cnf.use_tempaddr >= 2)) {
+- score.attrs |= IPV6_SADDR_SCORE_PRIVACY;
+- if (!(hiscore.attrs & IPV6_SADDR_SCORE_PRIVACY)) {
+- score.rule = 7;
+- goto record_it;
+- }
+- } else {
+- if (hiscore.attrs & IPV6_SADDR_SCORE_PRIVACY)
+- continue;
+- }
+-#else
+- if (hiscore.rule < 7)
+- hiscore.rule++;
+-#endif
+- /* Rule 8: Use longest matching prefix */
+- if (hiscore.rule < 8) {
+- hiscore.matchlen = ipv6_addr_diff(&ifa_result->addr, daddr);
+- hiscore.rule++;
+- }
+- score.matchlen = ipv6_addr_diff(&ifa->addr, daddr);
+- if (score.matchlen > hiscore.matchlen) {
+- score.rule = 8;
+- goto record_it;
+- }
+-#if 0
+- else if (score.matchlen < hiscore.matchlen)
+- continue;
+-#endif
+-
+- /* Final Rule: choose first available one */
+- continue;
+-record_it:
+- if (ifa_result)
+- in6_ifa_put(ifa_result);
+- in6_ifa_hold(ifa);
+- ifa_result = ifa;
+- hiscore = score;
+- }
+- read_unlock_bh(&idev->lock);
+- }
+- rcu_read_unlock();
+- read_unlock(&dev_base_lock);
+-
+- if (!ifa_result)
+- return -EADDRNOTAVAIL;
+-
+- ipv6_addr_copy(saddr, &ifa_result->addr);
+- in6_ifa_put(ifa_result);
+- return 0;
+-}
+-
+-
+-int ipv6_get_saddr(struct dst_entry *dst,
+- struct in6_addr *daddr, struct in6_addr *saddr)
+-{
+- return ipv6_dev_get_saddr(dst ? ip6_dst_idev(dst)->dev : NULL, daddr, saddr);
+-}
+-
+-EXPORT_SYMBOL(ipv6_get_saddr);
+-
+-int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr,
+- unsigned char banned_flags)
+-{
+- struct inet6_dev *idev;
+- int err = -EADDRNOTAVAIL;
+-
+- rcu_read_lock();
+- if ((idev = __in6_dev_get(dev)) != NULL) {
+- struct inet6_ifaddr *ifp;
+-
+- read_lock_bh(&idev->lock);
+- for (ifp=idev->addr_list; ifp; ifp=ifp->if_next) {
+- if (ifp->scope == IFA_LINK && !(ifp->flags & banned_flags)) {
+- ipv6_addr_copy(addr, &ifp->addr);
+- err = 0;
+- break;
+- }
+- }
+- read_unlock_bh(&idev->lock);
+- }
+- rcu_read_unlock();
+- return err;
+-}
+-
+-static int ipv6_count_addresses(struct inet6_dev *idev)
+-{
+- int cnt = 0;
+- struct inet6_ifaddr *ifp;
+-
+- read_lock_bh(&idev->lock);
+- for (ifp=idev->addr_list; ifp; ifp=ifp->if_next)
+- cnt++;
+- read_unlock_bh(&idev->lock);
+- return cnt;
+-}
+-
+-int ipv6_chk_addr(struct in6_addr *addr, struct net_device *dev, int strict)
+-{
+- struct inet6_ifaddr * ifp;
+- u8 hash = ipv6_addr_hash(addr);
+-
+- read_lock_bh(&addrconf_hash_lock);
+- for(ifp = inet6_addr_lst[hash]; ifp; ifp=ifp->lst_next) {
+- if (ipv6_addr_equal(&ifp->addr, addr) &&
+- !(ifp->flags&IFA_F_TENTATIVE)) {
+- if (dev == NULL || ifp->idev->dev == dev ||
+- !(ifp->scope&(IFA_LINK|IFA_HOST) || strict))
+- break;
+- }
+- }
+- read_unlock_bh(&addrconf_hash_lock);
+- return ifp != NULL;
+-}
+-
+-EXPORT_SYMBOL(ipv6_chk_addr);
+-
+-static
+-int ipv6_chk_same_addr(const struct in6_addr *addr, struct net_device *dev)
+-{
+- struct inet6_ifaddr * ifp;
+- u8 hash = ipv6_addr_hash(addr);
+-
+- for(ifp = inet6_addr_lst[hash]; ifp; ifp=ifp->lst_next) {
+- if (ipv6_addr_equal(&ifp->addr, addr)) {
+- if (dev == NULL || ifp->idev->dev == dev)
+- break;
+- }
+- }
+- return ifp != NULL;
+-}
+-
+-struct inet6_ifaddr * ipv6_get_ifaddr(struct in6_addr *addr, struct net_device *dev, int strict)
+-{
+- struct inet6_ifaddr * ifp;
+- u8 hash = ipv6_addr_hash(addr);
+-
+- read_lock_bh(&addrconf_hash_lock);
+- for(ifp = inet6_addr_lst[hash]; ifp; ifp=ifp->lst_next) {
+- if (ipv6_addr_equal(&ifp->addr, addr)) {
+- if (dev == NULL || ifp->idev->dev == dev ||
+- !(ifp->scope&(IFA_LINK|IFA_HOST) || strict)) {
+- in6_ifa_hold(ifp);
+- break;
+- }
+- }
+- }
+- read_unlock_bh(&addrconf_hash_lock);
+-
+- return ifp;
+-}
+-
+-int ipv6_rcv_saddr_equal(const struct sock *sk, const struct sock *sk2)
+-{
+- const struct in6_addr *sk_rcv_saddr6 = &inet6_sk(sk)->rcv_saddr;
+- const struct in6_addr *sk2_rcv_saddr6 = inet6_rcv_saddr(sk2);
+- __be32 sk_rcv_saddr = inet_sk(sk)->rcv_saddr;
+- __be32 sk2_rcv_saddr = inet_rcv_saddr(sk2);
+- int sk_ipv6only = ipv6_only_sock(sk);
+- int sk2_ipv6only = inet_v6_ipv6only(sk2);
+- int addr_type = ipv6_addr_type(sk_rcv_saddr6);
+- int addr_type2 = sk2_rcv_saddr6 ? ipv6_addr_type(sk2_rcv_saddr6) : IPV6_ADDR_MAPPED;
+-
+- if (!sk2_rcv_saddr && !sk_ipv6only)
+- return 1;
+-
+- if (addr_type2 == IPV6_ADDR_ANY &&
+- !(sk2_ipv6only && addr_type == IPV6_ADDR_MAPPED))
+- return 1;
+-
+- if (addr_type == IPV6_ADDR_ANY &&
+- !(sk_ipv6only && addr_type2 == IPV6_ADDR_MAPPED))
+- return 1;
+-
+- if (sk2_rcv_saddr6 &&
+- ipv6_addr_equal(sk_rcv_saddr6, sk2_rcv_saddr6))
+- return 1;
+-
+- if (addr_type == IPV6_ADDR_MAPPED &&
+- !sk2_ipv6only &&
+- (!sk2_rcv_saddr || !sk_rcv_saddr || sk_rcv_saddr == sk2_rcv_saddr))
+- return 1;
+-
+- return 0;
+-}
+-
+-/* Gets referenced address, destroys ifaddr */
+-
+-static void addrconf_dad_stop(struct inet6_ifaddr *ifp)
+-{
+- if (ifp->flags&IFA_F_PERMANENT) {
+- spin_lock_bh(&ifp->lock);
+- addrconf_del_timer(ifp);
+- ifp->flags |= IFA_F_TENTATIVE;
+- spin_unlock_bh(&ifp->lock);
+- in6_ifa_put(ifp);
+-#ifdef CONFIG_IPV6_PRIVACY
+- } else if (ifp->flags&IFA_F_TEMPORARY) {
+- struct inet6_ifaddr *ifpub;
+- spin_lock_bh(&ifp->lock);
+- ifpub = ifp->ifpub;
+- if (ifpub) {
+- in6_ifa_hold(ifpub);
+- spin_unlock_bh(&ifp->lock);
+- ipv6_create_tempaddr(ifpub, ifp);
+- in6_ifa_put(ifpub);
+- } else {
+- spin_unlock_bh(&ifp->lock);
+- }
+- ipv6_del_addr(ifp);
+-#endif
+- } else
+- ipv6_del_addr(ifp);
+-}
+-
+-void addrconf_dad_failure(struct inet6_ifaddr *ifp)
+-{
+- if (net_ratelimit())
+- printk(KERN_INFO "%s: duplicate address detected!\n", ifp->idev->dev->name);
+- addrconf_dad_stop(ifp);
+-}
+-
+-/* Join to solicited addr multicast group. */
+-
+-void addrconf_join_solict(struct net_device *dev, struct in6_addr *addr)
+-{
+- struct in6_addr maddr;
+-
+- if (dev->flags&(IFF_LOOPBACK|IFF_NOARP))
+- return;
+-
+- addrconf_addr_solict_mult(addr, &maddr);
+- ipv6_dev_mc_inc(dev, &maddr);
+-}
+-
+-void addrconf_leave_solict(struct inet6_dev *idev, struct in6_addr *addr)
+-{
+- struct in6_addr maddr;
+-
+- if (idev->dev->flags&(IFF_LOOPBACK|IFF_NOARP))
+- return;
+-
+- addrconf_addr_solict_mult(addr, &maddr);
+- __ipv6_dev_mc_dec(idev, &maddr);
+-}
+-
+-static void addrconf_join_anycast(struct inet6_ifaddr *ifp)
+-{
+- struct in6_addr addr;
+- ipv6_addr_prefix(&addr, &ifp->addr, ifp->prefix_len);
+- if (ipv6_addr_any(&addr))
+- return;
+- ipv6_dev_ac_inc(ifp->idev->dev, &addr);
+-}
+-
+-static void addrconf_leave_anycast(struct inet6_ifaddr *ifp)
+-{
+- struct in6_addr addr;
+- ipv6_addr_prefix(&addr, &ifp->addr, ifp->prefix_len);
+- if (ipv6_addr_any(&addr))
+- return;
+- __ipv6_dev_ac_dec(ifp->idev, &addr);
+-}
+-
+-static int addrconf_ifid_eui48(u8 *eui, struct net_device *dev)
+-{
+- if (dev->addr_len != ETH_ALEN)
+- return -1;
+- memcpy(eui, dev->dev_addr, 3);
+- memcpy(eui + 5, dev->dev_addr + 3, 3);
+-
+- /*
+- * The zSeries OSA network cards can be shared among various
+- * OS instances, but the OSA cards have only one MAC address.
+- * This leads to duplicate address conflicts in conjunction
+- * with IPv6 if more than one instance uses the same card.
+- *
+- * The driver for these cards can deliver a unique 16-bit
+- * identifier for each instance sharing the same card. It is
+- * placed instead of 0xFFFE in the interface identifier. The
+- * "u" bit of the interface identifier is not inverted in this
+- * case. Hence the resulting interface identifier has local
+- * scope according to RFC2373.
+- */
+- if (dev->dev_id) {
+- eui[3] = (dev->dev_id >> 8) & 0xFF;
+- eui[4] = dev->dev_id & 0xFF;
+- } else {
+- eui[3] = 0xFF;
+- eui[4] = 0xFE;
+- eui[0] ^= 2;
+- }
+- return 0;
+-}
+-
+-static int addrconf_ifid_arcnet(u8 *eui, struct net_device *dev)
+-{
+- /* XXX: inherit EUI-64 from other interface -- yoshfuji */
+- if (dev->addr_len != ARCNET_ALEN)
+- return -1;
+- memset(eui, 0, 7);
+- eui[7] = *(u8*)dev->dev_addr;
+- return 0;
+-}
+-
+-static int addrconf_ifid_infiniband(u8 *eui, struct net_device *dev)
+-{
+- if (dev->addr_len != INFINIBAND_ALEN)
+- return -1;
+- memcpy(eui, dev->dev_addr + 12, 8);
+- eui[0] |= 2;
+- return 0;
+-}
+-
+-static int ipv6_generate_eui64(u8 *eui, struct net_device *dev)
+-{
+- switch (dev->type) {
+- case ARPHRD_ETHER:
+- case ARPHRD_FDDI:
+- case ARPHRD_IEEE802_TR:
+- return addrconf_ifid_eui48(eui, dev);
+- case ARPHRD_ARCNET:
+- return addrconf_ifid_arcnet(eui, dev);
+- case ARPHRD_INFINIBAND:
+- return addrconf_ifid_infiniband(eui, dev);
+- }
+- return -1;
+-}
+-
+-static int ipv6_inherit_eui64(u8 *eui, struct inet6_dev *idev)
+-{
+- int err = -1;
+- struct inet6_ifaddr *ifp;
+-
+- read_lock_bh(&idev->lock);
+- for (ifp=idev->addr_list; ifp; ifp=ifp->if_next) {
+- if (ifp->scope == IFA_LINK && !(ifp->flags&IFA_F_TENTATIVE)) {
+- memcpy(eui, ifp->addr.s6_addr+8, 8);
+- err = 0;
+- break;
+- }
+- }
+- read_unlock_bh(&idev->lock);
+- return err;
+-}
+-
+-#ifdef CONFIG_IPV6_PRIVACY
+-/* (re)generation of randomized interface identifier (RFC 3041 3.2, 3.5) */
+-static int __ipv6_regen_rndid(struct inet6_dev *idev)
+-{
+-regen:
+- get_random_bytes(idev->rndid, sizeof(idev->rndid));
+- idev->rndid[0] &= ~0x02;
+-
+- /*
+- * <draft-ietf-ipngwg-temp-addresses-v2-00.txt>:
+- * check if generated address is not inappropriate
+- *
+- * - Reserved subnet anycast (RFC 2526)
+- * 11111101 11....11 1xxxxxxx
+- * - ISATAP (draft-ietf-ngtrans-isatap-13.txt) 5.1
+- * 00-00-5E-FE-xx-xx-xx-xx
+- * - value 0
+- * - XXX: already assigned to an address on the device
+- */
+- if (idev->rndid[0] == 0xfd &&
+- (idev->rndid[1]&idev->rndid[2]&idev->rndid[3]&idev->rndid[4]&idev->rndid[5]&idev->rndid[6]) == 0xff &&
+- (idev->rndid[7]&0x80))
+- goto regen;
+- if ((idev->rndid[0]|idev->rndid[1]) == 0) {
+- if (idev->rndid[2] == 0x5e && idev->rndid[3] == 0xfe)
+- goto regen;
+- if ((idev->rndid[2]|idev->rndid[3]|idev->rndid[4]|idev->rndid[5]|idev->rndid[6]|idev->rndid[7]) == 0x00)
+- goto regen;
+- }
+-
+- return 0;
+-}
+-
+-static void ipv6_regen_rndid(unsigned long data)
+-{
+- struct inet6_dev *idev = (struct inet6_dev *) data;
+- unsigned long expires;
+-
+- rcu_read_lock_bh();
+- write_lock_bh(&idev->lock);
+-
+- if (idev->dead)
+- goto out;
+-
+- if (__ipv6_regen_rndid(idev) < 0)
+- goto out;
+-
+- expires = jiffies +
+- idev->cnf.temp_prefered_lft * HZ -
+- idev->cnf.regen_max_retry * idev->cnf.dad_transmits * idev->nd_parms->retrans_time - desync_factor;
+- if (time_before(expires, jiffies)) {
+- printk(KERN_WARNING
+- "ipv6_regen_rndid(): too short regeneration interval; timer disabled for %s.\n",
+- idev->dev->name);
+- goto out;
+- }
+-
+- if (!mod_timer(&idev->regen_timer, expires))
+- in6_dev_hold(idev);
+-
+-out:
+- write_unlock_bh(&idev->lock);
+- rcu_read_unlock_bh();
+- in6_dev_put(idev);
+-}
+-
+-static int __ipv6_try_regen_rndid(struct inet6_dev *idev, struct in6_addr *tmpaddr) {
+- int ret = 0;
+-
+- if (tmpaddr && memcmp(idev->rndid, &tmpaddr->s6_addr[8], 8) == 0)
+- ret = __ipv6_regen_rndid(idev);
+- return ret;
+-}
+-#endif
+-
+-/*
+- * Add prefix route.
+- */
+-
+-static void
+-addrconf_prefix_route(struct in6_addr *pfx, int plen, struct net_device *dev,
+- unsigned long expires, u32 flags)
+-{
+- struct fib6_config cfg = {
+- .fc_table = RT6_TABLE_PREFIX,
+- .fc_metric = IP6_RT_PRIO_ADDRCONF,
+- .fc_ifindex = dev->ifindex,
+- .fc_expires = expires,
+- .fc_dst_len = plen,
+- .fc_flags = RTF_UP | flags,
+- };
+-
+- ipv6_addr_copy(&cfg.fc_dst, pfx);
+-
+- /* Prevent useless cloning on PtP SIT.
+- This thing is done here expecting that the whole
+- class of non-broadcast devices need not cloning.
+- */
+-#if defined(CONFIG_IPV6_SIT) || defined(CONFIG_IPV6_SIT_MODULE)
+- if (dev->type == ARPHRD_SIT && (dev->flags & IFF_POINTOPOINT))
+- cfg.fc_flags |= RTF_NONEXTHOP;
+-#endif
+-
+- ip6_route_add(&cfg);
+-}
+-
+-/* Create "default" multicast route to the interface */
+-
+-static void addrconf_add_mroute(struct net_device *dev)
+-{
+- struct fib6_config cfg = {
+- .fc_table = RT6_TABLE_LOCAL,
+- .fc_metric = IP6_RT_PRIO_ADDRCONF,
+- .fc_ifindex = dev->ifindex,
+- .fc_dst_len = 8,
+- .fc_flags = RTF_UP,
+- };
+-
+- ipv6_addr_set(&cfg.fc_dst, htonl(0xFF000000), 0, 0, 0);
+-
+- ip6_route_add(&cfg);
+-}
+-
+-#if defined(CONFIG_IPV6_SIT) || defined(CONFIG_IPV6_SIT_MODULE)
+-static void sit_route_add(struct net_device *dev)
+-{
+- struct fib6_config cfg = {
+- .fc_table = RT6_TABLE_MAIN,
+- .fc_metric = IP6_RT_PRIO_ADDRCONF,
+- .fc_ifindex = dev->ifindex,
+- .fc_dst_len = 96,
+- .fc_flags = RTF_UP | RTF_NONEXTHOP,
+- };
+-
+- /* prefix length - 96 bits "::d.d.d.d" */
+- ip6_route_add(&cfg);
+-}
+-#endif
+-
+-static void addrconf_add_lroute(struct net_device *dev)
+-{
+- struct in6_addr addr;
+-
+- ipv6_addr_set(&addr, htonl(0xFE800000), 0, 0, 0);
+- addrconf_prefix_route(&addr, 64, dev, 0, 0);
+-}
+-
+-static struct inet6_dev *addrconf_add_dev(struct net_device *dev)
+-{
+- struct inet6_dev *idev;
+-
+- ASSERT_RTNL();
+-
+- if ((idev = ipv6_find_idev(dev)) == NULL)
+- return NULL;
+-
+- /* Add default multicast route */
+- addrconf_add_mroute(dev);
+-
+- /* Add link local route */
+- addrconf_add_lroute(dev);
+- return idev;
+-}
+-
+-void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len)
+-{
+- struct prefix_info *pinfo;
+- __u32 valid_lft;
+- __u32 prefered_lft;
+- int addr_type;
+- unsigned long rt_expires;
+- struct inet6_dev *in6_dev;
+-
+- pinfo = (struct prefix_info *) opt;
+-
+- if (len < sizeof(struct prefix_info)) {
+- ADBG(("addrconf: prefix option too short\n"));
+- return;
+- }
+-
+- /*
+- * Validation checks ([ADDRCONF], page 19)
+- */
+-
+- addr_type = ipv6_addr_type(&pinfo->prefix);
+-
+- if (addr_type & (IPV6_ADDR_MULTICAST|IPV6_ADDR_LINKLOCAL))
+- return;
+-
+- valid_lft = ntohl(pinfo->valid);
+- prefered_lft = ntohl(pinfo->prefered);
+-
+- if (prefered_lft > valid_lft) {
+- if (net_ratelimit())
+- printk(KERN_WARNING "addrconf: prefix option has invalid lifetime\n");
+- return;
+- }
+-
+- in6_dev = in6_dev_get(dev);
+-
+- if (in6_dev == NULL) {
+- if (net_ratelimit())
+- printk(KERN_DEBUG "addrconf: device %s not configured\n", dev->name);
+- return;
+- }
+-
+- /*
+- * Two things going on here:
+- * 1) Add routes for on-link prefixes
+- * 2) Configure prefixes with the auto flag set
+- */
+-
+- /* Avoid arithmetic overflow. Really, we could
+- save rt_expires in seconds, likely valid_lft,
+- but it would require division in fib gc, that it
+- not good.
+- */
+- if (valid_lft >= 0x7FFFFFFF/HZ)
+- rt_expires = 0x7FFFFFFF - (0x7FFFFFFF % HZ);
+- else
+- rt_expires = valid_lft * HZ;
+-
+- /*
+- * We convert this (in jiffies) to clock_t later.
+- * Avoid arithmetic overflow there as well.
+- * Overflow can happen only if HZ < USER_HZ.
+- */
+- if (HZ < USER_HZ && rt_expires > 0x7FFFFFFF / USER_HZ)
+- rt_expires = 0x7FFFFFFF / USER_HZ;
+-
+- if (pinfo->onlink) {
+- struct rt6_info *rt;
+- rt = rt6_lookup(&pinfo->prefix, NULL, dev->ifindex, 1);
+-
+- if (rt && ((rt->rt6i_flags & (RTF_GATEWAY | RTF_DEFAULT)) == 0)) {
+- if (rt->rt6i_flags&RTF_EXPIRES) {
+- if (valid_lft == 0) {
+- ip6_del_rt(rt);
+- rt = NULL;
+- } else {
+- rt->rt6i_expires = jiffies + rt_expires;
+- }
+- }
+- } else if (valid_lft) {
+- addrconf_prefix_route(&pinfo->prefix, pinfo->prefix_len,
+- dev, jiffies_to_clock_t(rt_expires), RTF_ADDRCONF|RTF_EXPIRES|RTF_PREFIX_RT);
+- }
+- if (rt)
+- dst_release(&rt->u.dst);
+- }
+-
+- /* Try to figure out our local address for this prefix */
+-
+- if (pinfo->autoconf && in6_dev->cnf.autoconf) {
+- struct inet6_ifaddr * ifp;
+- struct in6_addr addr;
+- int create = 0, update_lft = 0;
+-
+- if (pinfo->prefix_len == 64) {
+- memcpy(&addr, &pinfo->prefix, 8);
+- if (ipv6_generate_eui64(addr.s6_addr + 8, dev) &&
+- ipv6_inherit_eui64(addr.s6_addr + 8, in6_dev)) {
+- in6_dev_put(in6_dev);
+- return;
+- }
+- goto ok;
+- }
+- if (net_ratelimit())
+- printk(KERN_DEBUG "IPv6 addrconf: prefix with wrong length %d\n",
+- pinfo->prefix_len);
+- in6_dev_put(in6_dev);
+- return;
+-
+-ok:
+-
+- ifp = ipv6_get_ifaddr(&addr, dev, 1);
+-
+- if (ifp == NULL && valid_lft) {
+- int max_addresses = in6_dev->cnf.max_addresses;
+- u32 addr_flags = 0;
+-
+-#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
+- if (in6_dev->cnf.optimistic_dad &&
+- !ipv6_devconf.forwarding)
+- addr_flags = IFA_F_OPTIMISTIC;
+-#endif
+-
+- /* Do not allow to create too much of autoconfigured
+- * addresses; this would be too easy way to crash kernel.
+- */
+- if (!max_addresses ||
+- ipv6_count_addresses(in6_dev) < max_addresses)
+- ifp = ipv6_add_addr(in6_dev, &addr, pinfo->prefix_len,
+- addr_type&IPV6_ADDR_SCOPE_MASK,
+- addr_flags);
+-
+- if (!ifp || IS_ERR(ifp)) {
+- in6_dev_put(in6_dev);
+- return;
+- }
+-
+- update_lft = create = 1;
+- ifp->cstamp = jiffies;
+- addrconf_dad_start(ifp, RTF_ADDRCONF|RTF_PREFIX_RT);
+- }
+-
+- if (ifp) {
+- int flags;
+- unsigned long now;
+-#ifdef CONFIG_IPV6_PRIVACY
+- struct inet6_ifaddr *ift;
+-#endif
+- u32 stored_lft;
+-
+- /* update lifetime (RFC2462 5.5.3 e) */
+- spin_lock(&ifp->lock);
+- now = jiffies;
+- if (ifp->valid_lft > (now - ifp->tstamp) / HZ)
+- stored_lft = ifp->valid_lft - (now - ifp->tstamp) / HZ;
+- else
+- stored_lft = 0;
+- if (!update_lft && stored_lft) {
+- if (valid_lft > MIN_VALID_LIFETIME ||
+- valid_lft > stored_lft)
+- update_lft = 1;
+- else if (stored_lft <= MIN_VALID_LIFETIME) {
+- /* valid_lft <= stored_lft is always true */
+- /* XXX: IPsec */
+- update_lft = 0;
+- } else {
+- valid_lft = MIN_VALID_LIFETIME;
+- if (valid_lft < prefered_lft)
+- prefered_lft = valid_lft;
+- update_lft = 1;
+- }
+- }
+-
+- if (update_lft) {
+- ifp->valid_lft = valid_lft;
+- ifp->prefered_lft = prefered_lft;
+- ifp->tstamp = now;
+- flags = ifp->flags;
+- ifp->flags &= ~IFA_F_DEPRECATED;
+- spin_unlock(&ifp->lock);
+-
+- if (!(flags&IFA_F_TENTATIVE))
+- ipv6_ifa_notify(0, ifp);
+- } else
+- spin_unlock(&ifp->lock);
+-
+-#ifdef CONFIG_IPV6_PRIVACY
+- read_lock_bh(&in6_dev->lock);
+- /* update all temporary addresses in the list */
+- for (ift=in6_dev->tempaddr_list; ift; ift=ift->tmp_next) {
+- /*
+- * When adjusting the lifetimes of an existing
+- * temporary address, only lower the lifetimes.
+- * Implementations must not increase the
+- * lifetimes of an existing temporary address
+- * when processing a Prefix Information Option.
+- */
+- spin_lock(&ift->lock);
+- flags = ift->flags;
+- if (ift->valid_lft > valid_lft &&
+- ift->valid_lft - valid_lft > (jiffies - ift->tstamp) / HZ)
+- ift->valid_lft = valid_lft + (jiffies - ift->tstamp) / HZ;
+- if (ift->prefered_lft > prefered_lft &&
+- ift->prefered_lft - prefered_lft > (jiffies - ift->tstamp) / HZ)
+- ift->prefered_lft = prefered_lft + (jiffies - ift->tstamp) / HZ;
+- spin_unlock(&ift->lock);
+- if (!(flags&IFA_F_TENTATIVE))
+- ipv6_ifa_notify(0, ift);
+- }
+-
+- if (create && in6_dev->cnf.use_tempaddr > 0) {
+- /*
+- * When a new public address is created as described in [ADDRCONF],
+- * also create a new temporary address.
+- */
+- read_unlock_bh(&in6_dev->lock);
+- ipv6_create_tempaddr(ifp, NULL);
+- } else {
+- read_unlock_bh(&in6_dev->lock);
+- }
+-#endif
+- in6_ifa_put(ifp);
+- addrconf_verify(0);
+- }
+- }
+- inet6_prefix_notify(RTM_NEWPREFIX, in6_dev, pinfo);
+- in6_dev_put(in6_dev);
+-}
+-
+-/*
+- * Set destination address.
+- * Special case for SIT interfaces where we create a new "virtual"
+- * device.
+- */
+-int addrconf_set_dstaddr(void __user *arg)
+-{
+- struct in6_ifreq ireq;
+- struct net_device *dev;
+- int err = -EINVAL;
+-
+- rtnl_lock();
+-
+- err = -EFAULT;
+- if (copy_from_user(&ireq, arg, sizeof(struct in6_ifreq)))
+- goto err_exit;
+-
+- dev = __dev_get_by_index(ireq.ifr6_ifindex);
+-
+- err = -ENODEV;
+- if (dev == NULL)
+- goto err_exit;
+-
+-#if defined(CONFIG_IPV6_SIT) || defined(CONFIG_IPV6_SIT_MODULE)
+- if (dev->type == ARPHRD_SIT) {
+- struct ifreq ifr;
+- mm_segment_t oldfs;
+- struct ip_tunnel_parm p;
+-
+- err = -EADDRNOTAVAIL;
+- if (!(ipv6_addr_type(&ireq.ifr6_addr) & IPV6_ADDR_COMPATv4))
+- goto err_exit;
+-
+- memset(&p, 0, sizeof(p));
+- p.iph.daddr = ireq.ifr6_addr.s6_addr32[3];
+- p.iph.saddr = 0;
+- p.iph.version = 4;
+- p.iph.ihl = 5;
+- p.iph.protocol = IPPROTO_IPV6;
+- p.iph.ttl = 64;
+- ifr.ifr_ifru.ifru_data = (void __user *)&p;
+-
+- oldfs = get_fs(); set_fs(KERNEL_DS);
+- err = dev->do_ioctl(dev, &ifr, SIOCADDTUNNEL);
+- set_fs(oldfs);
+-
+- if (err == 0) {
+- err = -ENOBUFS;
+- if ((dev = __dev_get_by_name(p.name)) == NULL)
+- goto err_exit;
+- err = dev_open(dev);
+- }
+- }
+-#endif
+-
+-err_exit:
+- rtnl_unlock();
+- return err;
+-}
+-
+-/*
+- * Manual configuration of address on an interface
+- */
+-static int inet6_addr_add(int ifindex, struct in6_addr *pfx, int plen,
+- __u8 ifa_flags, __u32 prefered_lft, __u32 valid_lft)
+-{
+- struct inet6_ifaddr *ifp;
+- struct inet6_dev *idev;
+- struct net_device *dev;
+- int scope;
+- u32 flags = RTF_EXPIRES;
+-
+- ASSERT_RTNL();
+-
+- /* check the lifetime */
+- if (!valid_lft || prefered_lft > valid_lft)
+- return -EINVAL;
+-
+- if ((dev = __dev_get_by_index(ifindex)) == NULL)
+- return -ENODEV;
+-
+- if ((idev = addrconf_add_dev(dev)) == NULL)
+- return -ENOBUFS;
+-
+- scope = ipv6_addr_scope(pfx);
+-
+- if (valid_lft == INFINITY_LIFE_TIME) {
+- ifa_flags |= IFA_F_PERMANENT;
+- flags = 0;
+- } else if (valid_lft >= 0x7FFFFFFF/HZ)
+- valid_lft = 0x7FFFFFFF/HZ;
+-
+- if (prefered_lft == 0)
+- ifa_flags |= IFA_F_DEPRECATED;
+- else if ((prefered_lft >= 0x7FFFFFFF/HZ) &&
+- (prefered_lft != INFINITY_LIFE_TIME))
+- prefered_lft = 0x7FFFFFFF/HZ;
+-
+- ifp = ipv6_add_addr(idev, pfx, plen, scope, ifa_flags);
+-
+- if (!IS_ERR(ifp)) {
+- spin_lock_bh(&ifp->lock);
+- ifp->valid_lft = valid_lft;
+- ifp->prefered_lft = prefered_lft;
+- ifp->tstamp = jiffies;
+- spin_unlock_bh(&ifp->lock);
+-
+- addrconf_prefix_route(&ifp->addr, ifp->prefix_len, dev,
+- jiffies_to_clock_t(valid_lft * HZ), flags);
+- /*
+- * Note that section 3.1 of RFC 4429 indicates
+- * that the Optimistic flag should not be set for
+- * manually configured addresses
+- */
+- addrconf_dad_start(ifp, 0);
+- in6_ifa_put(ifp);
+- addrconf_verify(0);
+- return 0;
+- }
+-
+- return PTR_ERR(ifp);
+-}
+-
+-static int inet6_addr_del(int ifindex, struct in6_addr *pfx, int plen)
+-{
+- struct inet6_ifaddr *ifp;
+- struct inet6_dev *idev;
+- struct net_device *dev;
+-
+- if ((dev = __dev_get_by_index(ifindex)) == NULL)
+- return -ENODEV;
+-
+- if ((idev = __in6_dev_get(dev)) == NULL)
+- return -ENXIO;
+-
+- read_lock_bh(&idev->lock);
+- for (ifp = idev->addr_list; ifp; ifp=ifp->if_next) {
+- if (ifp->prefix_len == plen &&
+- ipv6_addr_equal(pfx, &ifp->addr)) {
+- in6_ifa_hold(ifp);
+- read_unlock_bh(&idev->lock);
+-
+- ipv6_del_addr(ifp);
+-
+- /* If the last address is deleted administratively,
+- disable IPv6 on this interface.
+- */
+- if (idev->addr_list == NULL)
+- addrconf_ifdown(idev->dev, 1);
+- return 0;
+- }
+- }
+- read_unlock_bh(&idev->lock);
+- return -EADDRNOTAVAIL;
+-}
+-
+-
+-int addrconf_add_ifaddr(void __user *arg)
+-{
+- struct in6_ifreq ireq;
+- int err;
+-
+- if (!capable(CAP_NET_ADMIN))
+- return -EPERM;
+-
+- if (copy_from_user(&ireq, arg, sizeof(struct in6_ifreq)))
+- return -EFAULT;
+-
+- rtnl_lock();
+- err = inet6_addr_add(ireq.ifr6_ifindex, &ireq.ifr6_addr, ireq.ifr6_prefixlen,
+- IFA_F_PERMANENT, INFINITY_LIFE_TIME, INFINITY_LIFE_TIME);
+- rtnl_unlock();
+- return err;
+-}
+-
+-int addrconf_del_ifaddr(void __user *arg)
+-{
+- struct in6_ifreq ireq;
+- int err;
+-
+- if (!capable(CAP_NET_ADMIN))
+- return -EPERM;
+-
+- if (copy_from_user(&ireq, arg, sizeof(struct in6_ifreq)))
+- return -EFAULT;
+-
+- rtnl_lock();
+- err = inet6_addr_del(ireq.ifr6_ifindex, &ireq.ifr6_addr, ireq.ifr6_prefixlen);
+- rtnl_unlock();
+- return err;
+-}
+-
+-#if defined(CONFIG_IPV6_SIT) || defined(CONFIG_IPV6_SIT_MODULE)
+-static void sit_add_v4_addrs(struct inet6_dev *idev)
+-{
+- struct inet6_ifaddr * ifp;
+- struct in6_addr addr;
+- struct net_device *dev;
+- int scope;
+-
+- ASSERT_RTNL();
+-
+- memset(&addr, 0, sizeof(struct in6_addr));
+- memcpy(&addr.s6_addr32[3], idev->dev->dev_addr, 4);
+-
+- if (idev->dev->flags&IFF_POINTOPOINT) {
+- addr.s6_addr32[0] = htonl(0xfe800000);
+- scope = IFA_LINK;
+- } else {
+- scope = IPV6_ADDR_COMPATv4;
+- }
+-
+- if (addr.s6_addr32[3]) {
+- ifp = ipv6_add_addr(idev, &addr, 128, scope, IFA_F_PERMANENT);
+- if (!IS_ERR(ifp)) {
+- spin_lock_bh(&ifp->lock);
+- ifp->flags &= ~IFA_F_TENTATIVE;
+- spin_unlock_bh(&ifp->lock);
+- ipv6_ifa_notify(RTM_NEWADDR, ifp);
+- in6_ifa_put(ifp);
+- }
+- return;
+- }
+-
+- for_each_netdev(dev) {
+- struct in_device * in_dev = __in_dev_get_rtnl(dev);
+- if (in_dev && (dev->flags & IFF_UP)) {
+- struct in_ifaddr * ifa;
+-
+- int flag = scope;
+-
+- for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
+- int plen;
+-
+- addr.s6_addr32[3] = ifa->ifa_local;
+-
+- if (ifa->ifa_scope == RT_SCOPE_LINK)
+- continue;
+- if (ifa->ifa_scope >= RT_SCOPE_HOST) {
+- if (idev->dev->flags&IFF_POINTOPOINT)
+- continue;
+- flag |= IFA_HOST;
+- }
+- if (idev->dev->flags&IFF_POINTOPOINT)
+- plen = 64;
+- else
+- plen = 96;
+-
+- ifp = ipv6_add_addr(idev, &addr, plen, flag,
+- IFA_F_PERMANENT);
+- if (!IS_ERR(ifp)) {
+- spin_lock_bh(&ifp->lock);
+- ifp->flags &= ~IFA_F_TENTATIVE;
+- spin_unlock_bh(&ifp->lock);
+- ipv6_ifa_notify(RTM_NEWADDR, ifp);
+- in6_ifa_put(ifp);
+- }
+- }
+- }
+- }
+-}
+-#endif
+-
+-static void init_loopback(struct net_device *dev)
+-{
+- struct inet6_dev *idev;
+- struct inet6_ifaddr * ifp;
+-
+- /* ::1 */
+-
+- ASSERT_RTNL();
+-
+- if ((idev = ipv6_find_idev(dev)) == NULL) {
+- printk(KERN_DEBUG "init loopback: add_dev failed\n");
+- return;
+- }
+-
+- ifp = ipv6_add_addr(idev, &in6addr_loopback, 128, IFA_HOST, IFA_F_PERMANENT);
+- if (!IS_ERR(ifp)) {
+- spin_lock_bh(&ifp->lock);
+- ifp->flags &= ~IFA_F_TENTATIVE;
+- spin_unlock_bh(&ifp->lock);
+- ipv6_ifa_notify(RTM_NEWADDR, ifp);
+- in6_ifa_put(ifp);
+- }
+-}
+-
+-static void addrconf_add_linklocal(struct inet6_dev *idev, struct in6_addr *addr)
+-{
+- struct inet6_ifaddr * ifp;
+- u32 addr_flags = IFA_F_PERMANENT;
+-
+-#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
+- if (idev->cnf.optimistic_dad &&
+- !ipv6_devconf.forwarding)
+- addr_flags |= IFA_F_OPTIMISTIC;
+-#endif
+-
+-
+- ifp = ipv6_add_addr(idev, addr, 64, IFA_LINK, addr_flags);
+- if (!IS_ERR(ifp)) {
+- addrconf_prefix_route(&ifp->addr, ifp->prefix_len, idev->dev, 0, 0);
+- addrconf_dad_start(ifp, 0);
+- in6_ifa_put(ifp);
+- }
+-}
+-
+-static void addrconf_dev_config(struct net_device *dev)
+-{
+- struct in6_addr addr;
+- struct inet6_dev * idev;
+-
+- ASSERT_RTNL();
+-
+- if ((dev->type != ARPHRD_ETHER) &&
+- (dev->type != ARPHRD_FDDI) &&
+- (dev->type != ARPHRD_IEEE802_TR) &&
+- (dev->type != ARPHRD_ARCNET) &&
+- (dev->type != ARPHRD_INFINIBAND)) {
+- /* Alas, we support only Ethernet autoconfiguration. */
+- return;
+- }
+-
+- idev = addrconf_add_dev(dev);
+- if (idev == NULL)
+- return;
+-
+- memset(&addr, 0, sizeof(struct in6_addr));
+- addr.s6_addr32[0] = htonl(0xFE800000);
+-
+- if (ipv6_generate_eui64(addr.s6_addr + 8, dev) == 0)
+- addrconf_add_linklocal(idev, &addr);
+-}
+-
+-#if defined(CONFIG_IPV6_SIT) || defined(CONFIG_IPV6_SIT_MODULE)
+-static void addrconf_sit_config(struct net_device *dev)
+-{
+- struct inet6_dev *idev;
+-
+- ASSERT_RTNL();
+-
+- /*
+- * Configure the tunnel with one of our IPv4
+- * addresses... we should configure all of
+- * our v4 addrs in the tunnel
+- */
+-
+- if ((idev = ipv6_find_idev(dev)) == NULL) {
+- printk(KERN_DEBUG "init sit: add_dev failed\n");
+- return;
+- }
+-
+- sit_add_v4_addrs(idev);
+-
+- if (dev->flags&IFF_POINTOPOINT) {
+- addrconf_add_mroute(dev);
+- addrconf_add_lroute(dev);
+- } else
+- sit_route_add(dev);
+-}
+-#endif
+-
+-static inline int
+-ipv6_inherit_linklocal(struct inet6_dev *idev, struct net_device *link_dev)
+-{
+- struct in6_addr lladdr;
+-
+- if (!ipv6_get_lladdr(link_dev, &lladdr, IFA_F_TENTATIVE)) {
+- addrconf_add_linklocal(idev, &lladdr);
+- return 0;
+- }
+- return -1;
+-}
+-
+-static void ip6_tnl_add_linklocal(struct inet6_dev *idev)
+-{
+- struct net_device *link_dev;
+-
+- /* first try to inherit the link-local address from the link device */
+- if (idev->dev->iflink &&
+- (link_dev = __dev_get_by_index(idev->dev->iflink))) {
+- if (!ipv6_inherit_linklocal(idev, link_dev))
+- return;
+- }
+- /* then try to inherit it from any device */
+- for_each_netdev(link_dev) {
+- if (!ipv6_inherit_linklocal(idev, link_dev))
+- return;
+- }
+- printk(KERN_DEBUG "init ip6-ip6: add_linklocal failed\n");
+-}
+-
+-/*
+- * Autoconfigure tunnel with a link-local address so routing protocols,
+- * DHCPv6, MLD etc. can be run over the virtual link
+- */
+-
+-static void addrconf_ip6_tnl_config(struct net_device *dev)
+-{
+- struct inet6_dev *idev;
+-
+- ASSERT_RTNL();
+-
+- if ((idev = addrconf_add_dev(dev)) == NULL) {
+- printk(KERN_DEBUG "init ip6-ip6: add_dev failed\n");
+- return;
+- }
+- ip6_tnl_add_linklocal(idev);
+-}
+-
+-static int addrconf_notify(struct notifier_block *this, unsigned long event,
+- void * data)
+-{
+- struct net_device *dev = (struct net_device *) data;
+- struct inet6_dev *idev = __in6_dev_get(dev);
+- int run_pending = 0;
+-
+- switch(event) {
+- case NETDEV_REGISTER:
+- if (!idev && dev->mtu >= IPV6_MIN_MTU) {
+- idev = ipv6_add_dev(dev);
+- if (!idev)
+- printk(KERN_WARNING "IPv6: add_dev failed for %s\n",
+- dev->name);
+- }
+- break;
+- case NETDEV_UP:
+- case NETDEV_CHANGE:
+- if (event == NETDEV_UP) {
+- if (!addrconf_qdisc_ok(dev)) {
+- /* device is not ready yet. */
+- printk(KERN_INFO
+- "ADDRCONF(NETDEV_UP): %s: "
+- "link is not ready\n",
+- dev->name);
+- break;
+- }
+-
+- if (idev)
+- idev->if_flags |= IF_READY;
+- } else {
+- if (!addrconf_qdisc_ok(dev)) {
+- /* device is still not ready. */
+- break;
+- }
+-
+- if (idev) {
+- if (idev->if_flags & IF_READY) {
+- /* device is already configured. */
+- break;
+- }
+- idev->if_flags |= IF_READY;
+- }
+-
+- printk(KERN_INFO
+- "ADDRCONF(NETDEV_CHANGE): %s: "
+- "link becomes ready\n",
+- dev->name);
+-
+- run_pending = 1;
+- }
+-
+- switch(dev->type) {
+-#if defined(CONFIG_IPV6_SIT) || defined(CONFIG_IPV6_SIT_MODULE)
+- case ARPHRD_SIT:
+- addrconf_sit_config(dev);
+- break;
+-#endif
+- case ARPHRD_TUNNEL6:
+- addrconf_ip6_tnl_config(dev);
+- break;
+- case ARPHRD_LOOPBACK:
+- init_loopback(dev);
+- break;
+-
+- default:
+- addrconf_dev_config(dev);
+- break;
+- }
+- if (idev) {
+- if (run_pending)
+- addrconf_dad_run(idev);
+-
+- /* If the MTU changed during the interface down, when the
+- interface up, the changed MTU must be reflected in the
+- idev as well as routers.
+- */
+- if (idev->cnf.mtu6 != dev->mtu && dev->mtu >= IPV6_MIN_MTU) {
+- rt6_mtu_change(dev, dev->mtu);
+- idev->cnf.mtu6 = dev->mtu;
+- }
+- idev->tstamp = jiffies;
+- inet6_ifinfo_notify(RTM_NEWLINK, idev);
+- /* If the changed mtu during down is lower than IPV6_MIN_MTU
+- stop IPv6 on this interface.
+- */
+- if (dev->mtu < IPV6_MIN_MTU)
+- addrconf_ifdown(dev, event != NETDEV_DOWN);
+- }
+- break;
+-
+- case NETDEV_CHANGEMTU:
+- if ( idev && dev->mtu >= IPV6_MIN_MTU) {
+- rt6_mtu_change(dev, dev->mtu);
+- idev->cnf.mtu6 = dev->mtu;
+- break;
+- }
+-
+- /* MTU falled under IPV6_MIN_MTU. Stop IPv6 on this interface. */
+-
+- case NETDEV_DOWN:
+- case NETDEV_UNREGISTER:
+- /*
+- * Remove all addresses from this interface.
+- */
+- addrconf_ifdown(dev, event != NETDEV_DOWN);
+- break;
+-
+- case NETDEV_CHANGENAME:
+- if (idev) {
+- snmp6_unregister_dev(idev);
+-#ifdef CONFIG_SYSCTL
+- addrconf_sysctl_unregister(&idev->cnf);
+- neigh_sysctl_unregister(idev->nd_parms);
+- neigh_sysctl_register(dev, idev->nd_parms,
+- NET_IPV6, NET_IPV6_NEIGH, "ipv6",
+- &ndisc_ifinfo_sysctl_change,
+- NULL);
+- addrconf_sysctl_register(idev, &idev->cnf);
+-#endif
+- snmp6_register_dev(idev);
+- }
+- break;
+- }
+-
+- return NOTIFY_OK;
+-}
+-
+-/*
+- * addrconf module should be notified of a device going up
+- */
+-static struct notifier_block ipv6_dev_notf = {
+- .notifier_call = addrconf_notify,
+- .priority = 0
+-};
+-
+-static int addrconf_ifdown(struct net_device *dev, int how)
+-{
+- struct inet6_dev *idev;
+- struct inet6_ifaddr *ifa, **bifa;
+- int i;
+-
+- ASSERT_RTNL();
+-
+- if (dev == &loopback_dev && how == 1)
+- how = 0;
+-
+- rt6_ifdown(dev);
+- neigh_ifdown(&nd_tbl, dev);
+-
+- idev = __in6_dev_get(dev);
+- if (idev == NULL)
+- return -ENODEV;
+-
+- /* Step 1: remove reference to ipv6 device from parent device.
+- Do not dev_put!
+- */
+- if (how == 1) {
+- idev->dead = 1;
+-
+- /* protected by rtnl_lock */
+- rcu_assign_pointer(dev->ip6_ptr, NULL);
+-
+- /* Step 1.5: remove snmp6 entry */
+- snmp6_unregister_dev(idev);
+-
+- }
+-
+- /* Step 2: clear hash table */
+- for (i=0; i<IN6_ADDR_HSIZE; i++) {
+- bifa = &inet6_addr_lst[i];
+-
+- write_lock_bh(&addrconf_hash_lock);
+- while ((ifa = *bifa) != NULL) {
+- if (ifa->idev == idev) {
+- *bifa = ifa->lst_next;
+- ifa->lst_next = NULL;
+- addrconf_del_timer(ifa);
+- in6_ifa_put(ifa);
+- continue;
+- }
+- bifa = &ifa->lst_next;
+- }
+- write_unlock_bh(&addrconf_hash_lock);
+- }
+-
+- write_lock_bh(&idev->lock);
+-
+- /* Step 3: clear flags for stateless addrconf */
+- if (how != 1)
+- idev->if_flags &= ~(IF_RS_SENT|IF_RA_RCVD|IF_READY);
+-
+- /* Step 4: clear address list */
+-#ifdef CONFIG_IPV6_PRIVACY
+- if (how == 1 && del_timer(&idev->regen_timer))
+- in6_dev_put(idev);
+-
+- /* clear tempaddr list */
+- while ((ifa = idev->tempaddr_list) != NULL) {
+- idev->tempaddr_list = ifa->tmp_next;
+- ifa->tmp_next = NULL;
+- ifa->dead = 1;
+- write_unlock_bh(&idev->lock);
+- spin_lock_bh(&ifa->lock);
+-
+- if (ifa->ifpub) {
+- in6_ifa_put(ifa->ifpub);
+- ifa->ifpub = NULL;
+- }
+- spin_unlock_bh(&ifa->lock);
+- in6_ifa_put(ifa);
+- write_lock_bh(&idev->lock);
+- }
+-#endif
+- while ((ifa = idev->addr_list) != NULL) {
+- idev->addr_list = ifa->if_next;
+- ifa->if_next = NULL;
+- ifa->dead = 1;
+- addrconf_del_timer(ifa);
+- write_unlock_bh(&idev->lock);
+-
+- __ipv6_ifa_notify(RTM_DELADDR, ifa);
+- atomic_notifier_call_chain(&inet6addr_chain, NETDEV_DOWN, ifa);
+- in6_ifa_put(ifa);
+-
+- write_lock_bh(&idev->lock);
+- }
+- write_unlock_bh(&idev->lock);
+-
+- /* Step 5: Discard multicast list */
+-
+- if (how == 1)
+- ipv6_mc_destroy_dev(idev);
+- else
+- ipv6_mc_down(idev);
+-
+- /* Step 5: netlink notification of this interface */
+- idev->tstamp = jiffies;
+- inet6_ifinfo_notify(RTM_DELLINK, idev);
+-
+- /* Shot the device (if unregistered) */
+-
+- if (how == 1) {
+-#ifdef CONFIG_SYSCTL
+- addrconf_sysctl_unregister(&idev->cnf);
+- neigh_sysctl_unregister(idev->nd_parms);
+-#endif
+- neigh_parms_release(&nd_tbl, idev->nd_parms);
+- neigh_ifdown(&nd_tbl, dev);
+- in6_dev_put(idev);
+- }
+- return 0;
+-}
+-
+-static void addrconf_rs_timer(unsigned long data)
+-{
+- struct inet6_ifaddr *ifp = (struct inet6_ifaddr *) data;
+-
+- if (ifp->idev->cnf.forwarding)
+- goto out;
+-
+- if (ifp->idev->if_flags & IF_RA_RCVD) {
+- /*
+- * Announcement received after solicitation
+- * was sent
+- */
+- goto out;
+- }
+-
+- spin_lock(&ifp->lock);
+- if (ifp->probes++ < ifp->idev->cnf.rtr_solicits) {
+- struct in6_addr all_routers;
+-
+- /* The wait after the last probe can be shorter */
+- addrconf_mod_timer(ifp, AC_RS,
+- (ifp->probes == ifp->idev->cnf.rtr_solicits) ?
+- ifp->idev->cnf.rtr_solicit_delay :
+- ifp->idev->cnf.rtr_solicit_interval);
+- spin_unlock(&ifp->lock);
+-
+- ipv6_addr_all_routers(&all_routers);
+-
+- ndisc_send_rs(ifp->idev->dev, &ifp->addr, &all_routers);
+- } else {
+- spin_unlock(&ifp->lock);
+- /*
+- * Note: we do not support deprecated "all on-link"
+- * assumption any longer.
+- */
+- printk(KERN_DEBUG "%s: no IPv6 routers present\n",
+- ifp->idev->dev->name);
+- }
+-
+-out:
+- in6_ifa_put(ifp);
+-}
+-
+-/*
+- * Duplicate Address Detection
+- */
+-static void addrconf_dad_kick(struct inet6_ifaddr *ifp)
+-{
+- unsigned long rand_num;
+- struct inet6_dev *idev = ifp->idev;
+-
+- if (ifp->flags & IFA_F_OPTIMISTIC)
+- rand_num = 0;
+- else
+- rand_num = net_random() % (idev->cnf.rtr_solicit_delay ? : 1);
+-
+- ifp->probes = idev->cnf.dad_transmits;
+- addrconf_mod_timer(ifp, AC_DAD, rand_num);
+-}
+-
+-static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags)
+-{
+- struct inet6_dev *idev = ifp->idev;
+- struct net_device *dev = idev->dev;
+-
+- addrconf_join_solict(dev, &ifp->addr);
+-
+- net_srandom(ifp->addr.s6_addr32[3]);
+-
+- read_lock_bh(&idev->lock);
+- if (ifp->dead)
+- goto out;
+- spin_lock_bh(&ifp->lock);
+-
+- if (dev->flags&(IFF_NOARP|IFF_LOOPBACK) ||
+- !(ifp->flags&IFA_F_TENTATIVE) ||
+- ifp->flags & IFA_F_NODAD) {
+- ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC);
+- spin_unlock_bh(&ifp->lock);
+- read_unlock_bh(&idev->lock);
+-
+- addrconf_dad_completed(ifp);
+- return;
+- }
+-
+- if (!(idev->if_flags & IF_READY)) {
+- spin_unlock_bh(&ifp->lock);
+- read_unlock_bh(&idev->lock);
+- /*
+- * If the defice is not ready:
+- * - keep it tentative if it is a permanent address.
+- * - otherwise, kill it.
+- */
+- in6_ifa_hold(ifp);
+- addrconf_dad_stop(ifp);
+- return;
+- }
+-
+- /*
+- * Optimistic nodes can start receiving
+- * Frames right away
+- */
+- if(ifp->flags & IFA_F_OPTIMISTIC)
+- ip6_ins_rt(ifp->rt);
+-
+- addrconf_dad_kick(ifp);
+- spin_unlock_bh(&ifp->lock);
+-out:
+- read_unlock_bh(&idev->lock);
+-}
+-
+-static void addrconf_dad_timer(unsigned long data)
+-{
+- struct inet6_ifaddr *ifp = (struct inet6_ifaddr *) data;
+- struct inet6_dev *idev = ifp->idev;
+- struct in6_addr unspec;
+- struct in6_addr mcaddr;
+-
+- read_lock_bh(&idev->lock);
+- if (idev->dead) {
+- read_unlock_bh(&idev->lock);
+- goto out;
+- }
+- spin_lock_bh(&ifp->lock);
+- if (ifp->probes == 0) {
+- /*
+- * DAD was successful
+- */
+-
+- ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC);
+- spin_unlock_bh(&ifp->lock);
+- read_unlock_bh(&idev->lock);
+-
+- addrconf_dad_completed(ifp);
+-
+- goto out;
+- }
+-
+- ifp->probes--;
+- addrconf_mod_timer(ifp, AC_DAD, ifp->idev->nd_parms->retrans_time);
+- spin_unlock_bh(&ifp->lock);
+- read_unlock_bh(&idev->lock);
+-
+- /* send a neighbour solicitation for our addr */
+- memset(&unspec, 0, sizeof(unspec));
+- addrconf_addr_solict_mult(&ifp->addr, &mcaddr);
+- ndisc_send_ns(ifp->idev->dev, NULL, &ifp->addr, &mcaddr, &unspec);
+-out:
+- in6_ifa_put(ifp);
+-}
+-
+-static void addrconf_dad_completed(struct inet6_ifaddr *ifp)
+-{
+- struct net_device * dev = ifp->idev->dev;
+-
+- /*
+- * Configure the address for reception. Now it is valid.
+- */
+-
+- ipv6_ifa_notify(RTM_NEWADDR, ifp);
+-
+- /* If added prefix is link local and forwarding is off,
+- start sending router solicitations.
+- */
+-
+- if (ifp->idev->cnf.forwarding == 0 &&
+- ifp->idev->cnf.rtr_solicits > 0 &&
+- (dev->flags&IFF_LOOPBACK) == 0 &&
+- (ipv6_addr_type(&ifp->addr) & IPV6_ADDR_LINKLOCAL)) {
+- struct in6_addr all_routers;
+-
+- ipv6_addr_all_routers(&all_routers);
+-
+- /*
+- * If a host as already performed a random delay
+- * [...] as part of DAD [...] there is no need
+- * to delay again before sending the first RS
+- */
+- ndisc_send_rs(ifp->idev->dev, &ifp->addr, &all_routers);
+-
+- spin_lock_bh(&ifp->lock);
+- ifp->probes = 1;
+- ifp->idev->if_flags |= IF_RS_SENT;
+- addrconf_mod_timer(ifp, AC_RS, ifp->idev->cnf.rtr_solicit_interval);
+- spin_unlock_bh(&ifp->lock);
+- }
+-}
+-
+-static void addrconf_dad_run(struct inet6_dev *idev) {
+- struct inet6_ifaddr *ifp;
+-
+- read_lock_bh(&idev->lock);
+- for (ifp = idev->addr_list; ifp; ifp = ifp->if_next) {
+- spin_lock_bh(&ifp->lock);
+- if (!(ifp->flags & IFA_F_TENTATIVE)) {
+- spin_unlock_bh(&ifp->lock);
+- continue;
+- }
+- spin_unlock_bh(&ifp->lock);
+- addrconf_dad_kick(ifp);
+- }
+- read_unlock_bh(&idev->lock);
+-}
+-
+-#ifdef CONFIG_PROC_FS
+-struct if6_iter_state {
+- int bucket;
+-};
+-
+-static struct inet6_ifaddr *if6_get_first(struct seq_file *seq)
+-{
+- struct inet6_ifaddr *ifa = NULL;
+- struct if6_iter_state *state = seq->private;
+-
+- for (state->bucket = 0; state->bucket < IN6_ADDR_HSIZE; ++state->bucket) {
+- ifa = inet6_addr_lst[state->bucket];
+- if (ifa)
+- break;
+- }
+- return ifa;
+-}
+-
+-static struct inet6_ifaddr *if6_get_next(struct seq_file *seq, struct inet6_ifaddr *ifa)
+-{
+- struct if6_iter_state *state = seq->private;
+-
+- ifa = ifa->lst_next;
+-try_again:
+- if (!ifa && ++state->bucket < IN6_ADDR_HSIZE) {
+- ifa = inet6_addr_lst[state->bucket];
+- goto try_again;
+- }
+- return ifa;
+-}
+-
+-static struct inet6_ifaddr *if6_get_idx(struct seq_file *seq, loff_t pos)
+-{
+- struct inet6_ifaddr *ifa = if6_get_first(seq);
+-
+- if (ifa)
+- while(pos && (ifa = if6_get_next(seq, ifa)) != NULL)
+- --pos;
+- return pos ? NULL : ifa;
+-}
+-
+-static void *if6_seq_start(struct seq_file *seq, loff_t *pos)
+-{
+- read_lock_bh(&addrconf_hash_lock);
+- return if6_get_idx(seq, *pos);
+-}
+-
+-static void *if6_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+-{
+- struct inet6_ifaddr *ifa;
+-
+- ifa = if6_get_next(seq, v);
+- ++*pos;
+- return ifa;
+-}
+-
+-static void if6_seq_stop(struct seq_file *seq, void *v)
+-{
+- read_unlock_bh(&addrconf_hash_lock);
+-}
+-
+-static int if6_seq_show(struct seq_file *seq, void *v)
+-{
+- struct inet6_ifaddr *ifp = (struct inet6_ifaddr *)v;
+- seq_printf(seq,
+- NIP6_SEQFMT " %02x %02x %02x %02x %8s\n",
+- NIP6(ifp->addr),
+- ifp->idev->dev->ifindex,
+- ifp->prefix_len,
+- ifp->scope,
+- ifp->flags,
+- ifp->idev->dev->name);
+- return 0;
+-}
+-
+-static struct seq_operations if6_seq_ops = {
+- .start = if6_seq_start,
+- .next = if6_seq_next,
+- .show = if6_seq_show,
+- .stop = if6_seq_stop,
+-};
+-
+-static int if6_seq_open(struct inode *inode, struct file *file)
+-{
+- struct seq_file *seq;
+- int rc = -ENOMEM;
+- struct if6_iter_state *s = kzalloc(sizeof(*s), GFP_KERNEL);
+-
+- if (!s)
+- goto out;
+-
+- rc = seq_open(file, &if6_seq_ops);
+- if (rc)
+- goto out_kfree;
+-
+- seq = file->private_data;
+- seq->private = s;
+-out:
+- return rc;
+-out_kfree:
+- kfree(s);
+- goto out;
+-}
+-
+-static const struct file_operations if6_fops = {
+- .owner = THIS_MODULE,
+- .open = if6_seq_open,
+- .read = seq_read,
+- .llseek = seq_lseek,
+- .release = seq_release_private,
+-};
+-
+-int __init if6_proc_init(void)
+-{
+- if (!proc_net_fops_create("if_inet6", S_IRUGO, &if6_fops))
+- return -ENOMEM;
+- return 0;
+-}
+-
+-void if6_proc_exit(void)
+-{
+- proc_net_remove("if_inet6");
+-}
+-#endif /* CONFIG_PROC_FS */
+-
+-#ifdef CONFIG_IPV6_MIP6
+-/* Check if address is a home address configured on any interface. */
+-int ipv6_chk_home_addr(struct in6_addr *addr)
+-{
+- int ret = 0;
+- struct inet6_ifaddr * ifp;
+- u8 hash = ipv6_addr_hash(addr);
+- read_lock_bh(&addrconf_hash_lock);
+- for (ifp = inet6_addr_lst[hash]; ifp; ifp = ifp->lst_next) {
+- if (ipv6_addr_cmp(&ifp->addr, addr) == 0 &&
+- (ifp->flags & IFA_F_HOMEADDRESS)) {
+- ret = 1;
+- break;
+- }
+- }
+- read_unlock_bh(&addrconf_hash_lock);
+- return ret;
+-}
+-#endif
+-
+-/*
+- * Periodic address status verification
+- */
+-
+-static void addrconf_verify(unsigned long foo)
+-{
+- struct inet6_ifaddr *ifp;
+- unsigned long now, next;
+- int i;
+-
+- spin_lock_bh(&addrconf_verify_lock);
+- now = jiffies;
+- next = now + ADDR_CHECK_FREQUENCY;
+-
+- del_timer(&addr_chk_timer);
+-
+- for (i=0; i < IN6_ADDR_HSIZE; i++) {
+-
+-restart:
+- read_lock(&addrconf_hash_lock);
+- for (ifp=inet6_addr_lst[i]; ifp; ifp=ifp->lst_next) {
+- unsigned long age;
+-#ifdef CONFIG_IPV6_PRIVACY
+- unsigned long regen_advance;
+-#endif
+-
+- if (ifp->flags & IFA_F_PERMANENT)
+- continue;
+-
+- spin_lock(&ifp->lock);
+- age = (now - ifp->tstamp) / HZ;
+-
+-#ifdef CONFIG_IPV6_PRIVACY
+- regen_advance = ifp->idev->cnf.regen_max_retry *
+- ifp->idev->cnf.dad_transmits *
+- ifp->idev->nd_parms->retrans_time / HZ;
+-#endif
+-
+- if (ifp->valid_lft != INFINITY_LIFE_TIME &&
+- age >= ifp->valid_lft) {
+- spin_unlock(&ifp->lock);
+- in6_ifa_hold(ifp);
+- read_unlock(&addrconf_hash_lock);
+- ipv6_del_addr(ifp);
+- goto restart;
+- } else if (ifp->prefered_lft == INFINITY_LIFE_TIME) {
+- spin_unlock(&ifp->lock);
+- continue;
+- } else if (age >= ifp->prefered_lft) {
+- /* jiffies - ifp->tsamp > age >= ifp->prefered_lft */
+- int deprecate = 0;
+-
+- if (!(ifp->flags&IFA_F_DEPRECATED)) {
+- deprecate = 1;
+- ifp->flags |= IFA_F_DEPRECATED;
+- }
+-
+- if (time_before(ifp->tstamp + ifp->valid_lft * HZ, next))
+- next = ifp->tstamp + ifp->valid_lft * HZ;
+-
+- spin_unlock(&ifp->lock);
+-
+- if (deprecate) {
+- in6_ifa_hold(ifp);
+- read_unlock(&addrconf_hash_lock);
+-
+- ipv6_ifa_notify(0, ifp);
+- in6_ifa_put(ifp);
+- goto restart;
+- }
+-#ifdef CONFIG_IPV6_PRIVACY
+- } else if ((ifp->flags&IFA_F_TEMPORARY) &&
+- !(ifp->flags&IFA_F_TENTATIVE)) {
+- if (age >= ifp->prefered_lft - regen_advance) {
+- struct inet6_ifaddr *ifpub = ifp->ifpub;
+- if (time_before(ifp->tstamp + ifp->prefered_lft * HZ, next))
+- next = ifp->tstamp + ifp->prefered_lft * HZ;
+- if (!ifp->regen_count && ifpub) {
+- ifp->regen_count++;
+- in6_ifa_hold(ifp);
+- in6_ifa_hold(ifpub);
+- spin_unlock(&ifp->lock);
+- read_unlock(&addrconf_hash_lock);
+- spin_lock(&ifpub->lock);
+- ifpub->regen_count = 0;
+- spin_unlock(&ifpub->lock);
+- ipv6_create_tempaddr(ifpub, ifp);
+- in6_ifa_put(ifpub);
+- in6_ifa_put(ifp);
+- goto restart;
+- }
+- } else if (time_before(ifp->tstamp + ifp->prefered_lft * HZ - regen_advance * HZ, next))
+- next = ifp->tstamp + ifp->prefered_lft * HZ - regen_advance * HZ;
+- spin_unlock(&ifp->lock);
+-#endif
+- } else {
+- /* ifp->prefered_lft <= ifp->valid_lft */
+- if (time_before(ifp->tstamp + ifp->prefered_lft * HZ, next))
+- next = ifp->tstamp + ifp->prefered_lft * HZ;
+- spin_unlock(&ifp->lock);
+- }
+- }
+- read_unlock(&addrconf_hash_lock);
+- }
+-
+- addr_chk_timer.expires = time_before(next, jiffies + HZ) ? jiffies + HZ : next;
+- add_timer(&addr_chk_timer);
+- spin_unlock_bh(&addrconf_verify_lock);
+-}
+-
+-static struct in6_addr *extract_addr(struct nlattr *addr, struct nlattr *local)
+-{
+- struct in6_addr *pfx = NULL;
+-
+- if (addr)
+- pfx = nla_data(addr);
+-
+- if (local) {
+- if (pfx && nla_memcmp(local, pfx, sizeof(*pfx)))
+- pfx = NULL;
+- else
+- pfx = nla_data(local);
+- }
+-
+- return pfx;
+-}
+-
+-static const struct nla_policy ifa_ipv6_policy[IFA_MAX+1] = {
+- [IFA_ADDRESS] = { .len = sizeof(struct in6_addr) },
+- [IFA_LOCAL] = { .len = sizeof(struct in6_addr) },
+- [IFA_CACHEINFO] = { .len = sizeof(struct ifa_cacheinfo) },
+-};
+-
+-static int
+-inet6_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+-{
+- struct ifaddrmsg *ifm;
+- struct nlattr *tb[IFA_MAX+1];
+- struct in6_addr *pfx;
+- int err;
+-
+- err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv6_policy);
+- if (err < 0)
+- return err;
+-
+- ifm = nlmsg_data(nlh);
+- pfx = extract_addr(tb[IFA_ADDRESS], tb[IFA_LOCAL]);
+- if (pfx == NULL)
+- return -EINVAL;
+-
+- return inet6_addr_del(ifm->ifa_index, pfx, ifm->ifa_prefixlen);
+-}
+-
+-static int inet6_addr_modify(struct inet6_ifaddr *ifp, u8 ifa_flags,
+- u32 prefered_lft, u32 valid_lft)
+-{
+- u32 flags = RTF_EXPIRES;
+-
+- if (!valid_lft || (prefered_lft > valid_lft))
+- return -EINVAL;
+-
+- if (valid_lft == INFINITY_LIFE_TIME) {
+- ifa_flags |= IFA_F_PERMANENT;
+- flags = 0;
+- } else if (valid_lft >= 0x7FFFFFFF/HZ)
+- valid_lft = 0x7FFFFFFF/HZ;
+-
+- if (prefered_lft == 0)
+- ifa_flags |= IFA_F_DEPRECATED;
+- else if ((prefered_lft >= 0x7FFFFFFF/HZ) &&
+- (prefered_lft != INFINITY_LIFE_TIME))
+- prefered_lft = 0x7FFFFFFF/HZ;
+-
+- spin_lock_bh(&ifp->lock);
+- ifp->flags = (ifp->flags & ~(IFA_F_DEPRECATED | IFA_F_PERMANENT | IFA_F_NODAD | IFA_F_HOMEADDRESS)) | ifa_flags;
+- ifp->tstamp = jiffies;
+- ifp->valid_lft = valid_lft;
+- ifp->prefered_lft = prefered_lft;
+-
+- spin_unlock_bh(&ifp->lock);
+- if (!(ifp->flags&IFA_F_TENTATIVE))
+- ipv6_ifa_notify(0, ifp);
+-
+- addrconf_prefix_route(&ifp->addr, ifp->prefix_len, ifp->idev->dev,
+- jiffies_to_clock_t(valid_lft * HZ), flags);
+- addrconf_verify(0);
+-
+- return 0;
+-}
+-
+-static int
+-inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+-{
+- struct ifaddrmsg *ifm;
+- struct nlattr *tb[IFA_MAX+1];
+- struct in6_addr *pfx;
+- struct inet6_ifaddr *ifa;
+- struct net_device *dev;
+- u32 valid_lft = INFINITY_LIFE_TIME, preferred_lft = INFINITY_LIFE_TIME;
+- u8 ifa_flags;
+- int err;
+-
+- err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv6_policy);
+- if (err < 0)
+- return err;
+-
+- ifm = nlmsg_data(nlh);
+- pfx = extract_addr(tb[IFA_ADDRESS], tb[IFA_LOCAL]);
+- if (pfx == NULL)
+- return -EINVAL;
+-
+- if (tb[IFA_CACHEINFO]) {
+- struct ifa_cacheinfo *ci;
+-
+- ci = nla_data(tb[IFA_CACHEINFO]);
+- valid_lft = ci->ifa_valid;
+- preferred_lft = ci->ifa_prefered;
+- } else {
+- preferred_lft = INFINITY_LIFE_TIME;
+- valid_lft = INFINITY_LIFE_TIME;
+- }
+-
+- dev = __dev_get_by_index(ifm->ifa_index);
+- if (dev == NULL)
+- return -ENODEV;
+-
+- /* We ignore other flags so far. */
+- ifa_flags = ifm->ifa_flags & (IFA_F_NODAD | IFA_F_HOMEADDRESS);
+-
+- ifa = ipv6_get_ifaddr(pfx, dev, 1);
+- if (ifa == NULL) {
+- /*
+- * It would be best to check for !NLM_F_CREATE here but
+- * userspace alreay relies on not having to provide this.
+- */
+- return inet6_addr_add(ifm->ifa_index, pfx, ifm->ifa_prefixlen,
+- ifa_flags, preferred_lft, valid_lft);
+- }
+-
+- if (nlh->nlmsg_flags & NLM_F_EXCL ||
+- !(nlh->nlmsg_flags & NLM_F_REPLACE))
+- err = -EEXIST;
+- else
+- err = inet6_addr_modify(ifa, ifa_flags, preferred_lft, valid_lft);
+-
+- in6_ifa_put(ifa);
+-
+- return err;
+-}
+-
+-static void put_ifaddrmsg(struct nlmsghdr *nlh, u8 prefixlen, u8 flags,
+- u8 scope, int ifindex)
+-{
+- struct ifaddrmsg *ifm;
+-
+- ifm = nlmsg_data(nlh);
+- ifm->ifa_family = AF_INET6;
+- ifm->ifa_prefixlen = prefixlen;
+- ifm->ifa_flags = flags;
+- ifm->ifa_scope = scope;
+- ifm->ifa_index = ifindex;
+-}
+-
+-static int put_cacheinfo(struct sk_buff *skb, unsigned long cstamp,
+- unsigned long tstamp, u32 preferred, u32 valid)
+-{
+- struct ifa_cacheinfo ci;
+-
+- ci.cstamp = (u32)(TIME_DELTA(cstamp, INITIAL_JIFFIES) / HZ * 100
+- + TIME_DELTA(cstamp, INITIAL_JIFFIES) % HZ * 100 / HZ);
+- ci.tstamp = (u32)(TIME_DELTA(tstamp, INITIAL_JIFFIES) / HZ * 100
+- + TIME_DELTA(tstamp, INITIAL_JIFFIES) % HZ * 100 / HZ);
+- ci.ifa_prefered = preferred;
+- ci.ifa_valid = valid;
+-
+- return nla_put(skb, IFA_CACHEINFO, sizeof(ci), &ci);
+-}
+-
+-static inline int rt_scope(int ifa_scope)
+-{
+- if (ifa_scope & IFA_HOST)
+- return RT_SCOPE_HOST;
+- else if (ifa_scope & IFA_LINK)
+- return RT_SCOPE_LINK;
+- else if (ifa_scope & IFA_SITE)
+- return RT_SCOPE_SITE;
+- else
+- return RT_SCOPE_UNIVERSE;
+-}
+-
+-static inline int inet6_ifaddr_msgsize(void)
+-{
+- return NLMSG_ALIGN(sizeof(struct ifaddrmsg))
+- + nla_total_size(16) /* IFA_ADDRESS */
+- + nla_total_size(sizeof(struct ifa_cacheinfo));
+-}
+-
+-static int inet6_fill_ifaddr(struct sk_buff *skb, struct inet6_ifaddr *ifa,
+- u32 pid, u32 seq, int event, unsigned int flags)
+-{
+- struct nlmsghdr *nlh;
+- u32 preferred, valid;
+-
+- nlh = nlmsg_put(skb, pid, seq, event, sizeof(struct ifaddrmsg), flags);
+- if (nlh == NULL)
+- return -EMSGSIZE;
+-
+- put_ifaddrmsg(nlh, ifa->prefix_len, ifa->flags, rt_scope(ifa->scope),
+- ifa->idev->dev->ifindex);
+-
+- if (!(ifa->flags&IFA_F_PERMANENT)) {
+- preferred = ifa->prefered_lft;
+- valid = ifa->valid_lft;
+- if (preferred != INFINITY_LIFE_TIME) {
+- long tval = (jiffies - ifa->tstamp)/HZ;
+- preferred -= tval;
+- if (valid != INFINITY_LIFE_TIME)
+- valid -= tval;
+- }
+- } else {
+- preferred = INFINITY_LIFE_TIME;
+- valid = INFINITY_LIFE_TIME;
+- }
+-
+- if (nla_put(skb, IFA_ADDRESS, 16, &ifa->addr) < 0 ||
+- put_cacheinfo(skb, ifa->cstamp, ifa->tstamp, preferred, valid) < 0) {
+- nlmsg_cancel(skb, nlh);
+- return -EMSGSIZE;
+- }
+-
+- return nlmsg_end(skb, nlh);
+-}
+-
+-static int inet6_fill_ifmcaddr(struct sk_buff *skb, struct ifmcaddr6 *ifmca,
+- u32 pid, u32 seq, int event, u16 flags)
+-{
+- struct nlmsghdr *nlh;
+- u8 scope = RT_SCOPE_UNIVERSE;
+- int ifindex = ifmca->idev->dev->ifindex;
+-
+- if (ipv6_addr_scope(&ifmca->mca_addr) & IFA_SITE)
+- scope = RT_SCOPE_SITE;
+-
+- nlh = nlmsg_put(skb, pid, seq, event, sizeof(struct ifaddrmsg), flags);
+- if (nlh == NULL)
+- return -EMSGSIZE;
+-
+- put_ifaddrmsg(nlh, 128, IFA_F_PERMANENT, scope, ifindex);
+- if (nla_put(skb, IFA_MULTICAST, 16, &ifmca->mca_addr) < 0 ||
+- put_cacheinfo(skb, ifmca->mca_cstamp, ifmca->mca_tstamp,
+- INFINITY_LIFE_TIME, INFINITY_LIFE_TIME) < 0) {
+- nlmsg_cancel(skb, nlh);
+- return -EMSGSIZE;
+- }
+-
+- return nlmsg_end(skb, nlh);
+-}
+-
+-static int inet6_fill_ifacaddr(struct sk_buff *skb, struct ifacaddr6 *ifaca,
+- u32 pid, u32 seq, int event, unsigned int flags)
+-{
+- struct nlmsghdr *nlh;
+- u8 scope = RT_SCOPE_UNIVERSE;
+- int ifindex = ifaca->aca_idev->dev->ifindex;
+-
+- if (ipv6_addr_scope(&ifaca->aca_addr) & IFA_SITE)
+- scope = RT_SCOPE_SITE;
+-
+- nlh = nlmsg_put(skb, pid, seq, event, sizeof(struct ifaddrmsg), flags);
+- if (nlh == NULL)
+- return -EMSGSIZE;
+-
+- put_ifaddrmsg(nlh, 128, IFA_F_PERMANENT, scope, ifindex);
+- if (nla_put(skb, IFA_ANYCAST, 16, &ifaca->aca_addr) < 0 ||
+- put_cacheinfo(skb, ifaca->aca_cstamp, ifaca->aca_tstamp,
+- INFINITY_LIFE_TIME, INFINITY_LIFE_TIME) < 0) {
+- nlmsg_cancel(skb, nlh);
+- return -EMSGSIZE;
+- }
+-
+- return nlmsg_end(skb, nlh);
+-}
+-
+-enum addr_type_t
+-{
+- UNICAST_ADDR,
+- MULTICAST_ADDR,
+- ANYCAST_ADDR,
+-};
+-
+-static int inet6_dump_addr(struct sk_buff *skb, struct netlink_callback *cb,
+- enum addr_type_t type)
+-{
+- int idx, ip_idx;
+- int s_idx, s_ip_idx;
+- int err = 1;
+- struct net_device *dev;
+- struct inet6_dev *idev = NULL;
+- struct inet6_ifaddr *ifa;
+- struct ifmcaddr6 *ifmca;
+- struct ifacaddr6 *ifaca;
+-
+- s_idx = cb->args[0];
+- s_ip_idx = ip_idx = cb->args[1];
+-
+- idx = 0;
+- for_each_netdev(dev) {
+- if (idx < s_idx)
+- goto cont;
+- if (idx > s_idx)
+- s_ip_idx = 0;
+- ip_idx = 0;
+- if ((idev = in6_dev_get(dev)) == NULL)
+- goto cont;
+- read_lock_bh(&idev->lock);
+- switch (type) {
+- case UNICAST_ADDR:
+- /* unicast address incl. temp addr */
+- for (ifa = idev->addr_list; ifa;
+- ifa = ifa->if_next, ip_idx++) {
+- if (ip_idx < s_ip_idx)
+- continue;
+- if ((err = inet6_fill_ifaddr(skb, ifa,
+- NETLINK_CB(cb->skb).pid,
+- cb->nlh->nlmsg_seq, RTM_NEWADDR,
+- NLM_F_MULTI)) <= 0)
+- goto done;
+- }
+- break;
+- case MULTICAST_ADDR:
+- /* multicast address */
+- for (ifmca = idev->mc_list; ifmca;
+- ifmca = ifmca->next, ip_idx++) {
+- if (ip_idx < s_ip_idx)
+- continue;
+- if ((err = inet6_fill_ifmcaddr(skb, ifmca,
+- NETLINK_CB(cb->skb).pid,
+- cb->nlh->nlmsg_seq, RTM_GETMULTICAST,
+- NLM_F_MULTI)) <= 0)
+- goto done;
+- }
+- break;
+- case ANYCAST_ADDR:
+- /* anycast address */
+- for (ifaca = idev->ac_list; ifaca;
+- ifaca = ifaca->aca_next, ip_idx++) {
+- if (ip_idx < s_ip_idx)
+- continue;
+- if ((err = inet6_fill_ifacaddr(skb, ifaca,
+- NETLINK_CB(cb->skb).pid,
+- cb->nlh->nlmsg_seq, RTM_GETANYCAST,
+- NLM_F_MULTI)) <= 0)
+- goto done;
+- }
+- break;
+- default:
+- break;
+- }
+- read_unlock_bh(&idev->lock);
+- in6_dev_put(idev);
+-cont:
+- idx++;
+- }
+-done:
+- if (err <= 0) {
+- read_unlock_bh(&idev->lock);
+- in6_dev_put(idev);
+- }
+- cb->args[0] = idx;
+- cb->args[1] = ip_idx;
+- return skb->len;
+-}
+-
+-static int inet6_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb)
+-{
+- enum addr_type_t type = UNICAST_ADDR;
+- return inet6_dump_addr(skb, cb, type);
+-}
+-
+-static int inet6_dump_ifmcaddr(struct sk_buff *skb, struct netlink_callback *cb)
+-{
+- enum addr_type_t type = MULTICAST_ADDR;
+- return inet6_dump_addr(skb, cb, type);
+-}
+-
+-
+-static int inet6_dump_ifacaddr(struct sk_buff *skb, struct netlink_callback *cb)
+-{
+- enum addr_type_t type = ANYCAST_ADDR;
+- return inet6_dump_addr(skb, cb, type);
+-}
+-
+-static int inet6_rtm_getaddr(struct sk_buff *in_skb, struct nlmsghdr* nlh,
+- void *arg)
+-{
+- struct ifaddrmsg *ifm;
+- struct nlattr *tb[IFA_MAX+1];
+- struct in6_addr *addr = NULL;
+- struct net_device *dev = NULL;
+- struct inet6_ifaddr *ifa;
+- struct sk_buff *skb;
+- int err;
+-
+- err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv6_policy);
+- if (err < 0)
+- goto errout;
+-
+- addr = extract_addr(tb[IFA_ADDRESS], tb[IFA_LOCAL]);
+- if (addr == NULL) {
+- err = -EINVAL;
+- goto errout;
+- }
+-
+- ifm = nlmsg_data(nlh);
+- if (ifm->ifa_index)
+- dev = __dev_get_by_index(ifm->ifa_index);
+-
+- if ((ifa = ipv6_get_ifaddr(addr, dev, 1)) == NULL) {
+- err = -EADDRNOTAVAIL;
+- goto errout;
+- }
+-
+- if ((skb = nlmsg_new(inet6_ifaddr_msgsize(), GFP_KERNEL)) == NULL) {
+- err = -ENOBUFS;
+- goto errout_ifa;
+- }
+-
+- err = inet6_fill_ifaddr(skb, ifa, NETLINK_CB(in_skb).pid,
+- nlh->nlmsg_seq, RTM_NEWADDR, 0);
+- if (err < 0) {
+- /* -EMSGSIZE implies BUG in inet6_ifaddr_msgsize() */
+- WARN_ON(err == -EMSGSIZE);
+- kfree_skb(skb);
+- goto errout_ifa;
+- }
+- err = rtnl_unicast(skb, NETLINK_CB(in_skb).pid);
+-errout_ifa:
+- in6_ifa_put(ifa);
+-errout:
+- return err;
+-}
+-
+-static void inet6_ifa_notify(int event, struct inet6_ifaddr *ifa)
+-{
+- struct sk_buff *skb;
+- int err = -ENOBUFS;
+-
+- skb = nlmsg_new(inet6_ifaddr_msgsize(), GFP_ATOMIC);
+- if (skb == NULL)
+- goto errout;
+-
+- err = inet6_fill_ifaddr(skb, ifa, 0, 0, event, 0);
+- if (err < 0) {
+- /* -EMSGSIZE implies BUG in inet6_ifaddr_msgsize() */
+- WARN_ON(err == -EMSGSIZE);
+- kfree_skb(skb);
+- goto errout;
+- }
+- err = rtnl_notify(skb, 0, RTNLGRP_IPV6_IFADDR, NULL, GFP_ATOMIC);
+-errout:
+- if (err < 0)
+- rtnl_set_sk_err(RTNLGRP_IPV6_IFADDR, err);
+-}
+-
+-static inline void ipv6_store_devconf(struct ipv6_devconf *cnf,
+- __s32 *array, int bytes)
+-{
+- BUG_ON(bytes < (DEVCONF_MAX * 4));
+-
+- memset(array, 0, bytes);
+- array[DEVCONF_FORWARDING] = cnf->forwarding;
+- array[DEVCONF_HOPLIMIT] = cnf->hop_limit;
+- array[DEVCONF_MTU6] = cnf->mtu6;
+- array[DEVCONF_ACCEPT_RA] = cnf->accept_ra;
+- array[DEVCONF_ACCEPT_REDIRECTS] = cnf->accept_redirects;
+- array[DEVCONF_AUTOCONF] = cnf->autoconf;
+- array[DEVCONF_DAD_TRANSMITS] = cnf->dad_transmits;
+- array[DEVCONF_RTR_SOLICITS] = cnf->rtr_solicits;
+- array[DEVCONF_RTR_SOLICIT_INTERVAL] = cnf->rtr_solicit_interval;
+- array[DEVCONF_RTR_SOLICIT_DELAY] = cnf->rtr_solicit_delay;
+- array[DEVCONF_FORCE_MLD_VERSION] = cnf->force_mld_version;
+-#ifdef CONFIG_IPV6_PRIVACY
+- array[DEVCONF_USE_TEMPADDR] = cnf->use_tempaddr;
+- array[DEVCONF_TEMP_VALID_LFT] = cnf->temp_valid_lft;
+- array[DEVCONF_TEMP_PREFERED_LFT] = cnf->temp_prefered_lft;
+- array[DEVCONF_REGEN_MAX_RETRY] = cnf->regen_max_retry;
+- array[DEVCONF_MAX_DESYNC_FACTOR] = cnf->max_desync_factor;
+-#endif
+- array[DEVCONF_MAX_ADDRESSES] = cnf->max_addresses;
+- array[DEVCONF_ACCEPT_RA_DEFRTR] = cnf->accept_ra_defrtr;
+- array[DEVCONF_ACCEPT_RA_PINFO] = cnf->accept_ra_pinfo;
+-#ifdef CONFIG_IPV6_ROUTER_PREF
+- array[DEVCONF_ACCEPT_RA_RTR_PREF] = cnf->accept_ra_rtr_pref;
+- array[DEVCONF_RTR_PROBE_INTERVAL] = cnf->rtr_probe_interval;
+-#ifdef CONFIG_IPV6_ROUTE_INFO
+- array[DEVCONF_ACCEPT_RA_RT_INFO_MAX_PLEN] = cnf->accept_ra_rt_info_max_plen;
+-#endif
+-#endif
+- array[DEVCONF_PROXY_NDP] = cnf->proxy_ndp;
+- array[DEVCONF_ACCEPT_SOURCE_ROUTE] = cnf->accept_source_route;
+-#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
+- array[DEVCONF_OPTIMISTIC_DAD] = cnf->optimistic_dad;
+-#endif
+-}
+-
+-static inline size_t inet6_if_nlmsg_size(void)
+-{
+- return NLMSG_ALIGN(sizeof(struct ifinfomsg))
+- + nla_total_size(IFNAMSIZ) /* IFLA_IFNAME */
+- + nla_total_size(MAX_ADDR_LEN) /* IFLA_ADDRESS */
+- + nla_total_size(4) /* IFLA_MTU */
+- + nla_total_size(4) /* IFLA_LINK */
+- + nla_total_size( /* IFLA_PROTINFO */
+- nla_total_size(4) /* IFLA_INET6_FLAGS */
+- + nla_total_size(sizeof(struct ifla_cacheinfo))
+- + nla_total_size(DEVCONF_MAX * 4) /* IFLA_INET6_CONF */
+- + nla_total_size(IPSTATS_MIB_MAX * 8) /* IFLA_INET6_STATS */
+- + nla_total_size(ICMP6_MIB_MAX * 8) /* IFLA_INET6_ICMP6STATS */
+- );
+-}
+-
+-static inline void __snmp6_fill_stats(u64 *stats, void **mib, int items,
+- int bytes)
+-{
+- int i;
+- int pad = bytes - sizeof(u64) * items;
+- BUG_ON(pad < 0);
+-
+- /* Use put_unaligned() because stats may not be aligned for u64. */
+- put_unaligned(items, &stats[0]);
+- for (i = 1; i < items; i++)
+- put_unaligned(snmp_fold_field(mib, i), &stats[i]);
+-
+- memset(&stats[items], 0, pad);
+-}
+-
+-static void snmp6_fill_stats(u64 *stats, struct inet6_dev *idev, int attrtype,
+- int bytes)
+-{
+- switch(attrtype) {
+- case IFLA_INET6_STATS:
+- __snmp6_fill_stats(stats, (void **)idev->stats.ipv6, IPSTATS_MIB_MAX, bytes);
+- break;
+- case IFLA_INET6_ICMP6STATS:
+- __snmp6_fill_stats(stats, (void **)idev->stats.icmpv6, ICMP6_MIB_MAX, bytes);
+- break;
+- }
+-}
+-
+-static int inet6_fill_ifinfo(struct sk_buff *skb, struct inet6_dev *idev,
+- u32 pid, u32 seq, int event, unsigned int flags)
+-{
+- struct net_device *dev = idev->dev;
+- struct nlattr *nla;
+- struct ifinfomsg *hdr;
+- struct nlmsghdr *nlh;
+- void *protoinfo;
+- struct ifla_cacheinfo ci;
+-
+- nlh = nlmsg_put(skb, pid, seq, event, sizeof(*hdr), flags);
+- if (nlh == NULL)
+- return -EMSGSIZE;
+-
+- hdr = nlmsg_data(nlh);
+- hdr->ifi_family = AF_INET6;
+- hdr->__ifi_pad = 0;
+- hdr->ifi_type = dev->type;
+- hdr->ifi_index = dev->ifindex;
+- hdr->ifi_flags = dev_get_flags(dev);
+- hdr->ifi_change = 0;
+-
+- NLA_PUT_STRING(skb, IFLA_IFNAME, dev->name);
+-
+- if (dev->addr_len)
+- NLA_PUT(skb, IFLA_ADDRESS, dev->addr_len, dev->dev_addr);
+-
+- NLA_PUT_U32(skb, IFLA_MTU, dev->mtu);
+- if (dev->ifindex != dev->iflink)
+- NLA_PUT_U32(skb, IFLA_LINK, dev->iflink);
+-
+- protoinfo = nla_nest_start(skb, IFLA_PROTINFO);
+- if (protoinfo == NULL)
+- goto nla_put_failure;
+-
+- NLA_PUT_U32(skb, IFLA_INET6_FLAGS, idev->if_flags);
+-
+- ci.max_reasm_len = IPV6_MAXPLEN;
+- ci.tstamp = (__u32)(TIME_DELTA(idev->tstamp, INITIAL_JIFFIES) / HZ * 100
+- + TIME_DELTA(idev->tstamp, INITIAL_JIFFIES) % HZ * 100 / HZ);
+- ci.reachable_time = idev->nd_parms->reachable_time;
+- ci.retrans_time = idev->nd_parms->retrans_time;
+- NLA_PUT(skb, IFLA_INET6_CACHEINFO, sizeof(ci), &ci);
+-
+- nla = nla_reserve(skb, IFLA_INET6_CONF, DEVCONF_MAX * sizeof(s32));
+- if (nla == NULL)
+- goto nla_put_failure;
+- ipv6_store_devconf(&idev->cnf, nla_data(nla), nla_len(nla));
+-
+- /* XXX - MC not implemented */
+-
+- nla = nla_reserve(skb, IFLA_INET6_STATS, IPSTATS_MIB_MAX * sizeof(u64));
+- if (nla == NULL)
+- goto nla_put_failure;
+- snmp6_fill_stats(nla_data(nla), idev, IFLA_INET6_STATS, nla_len(nla));
+-
+- nla = nla_reserve(skb, IFLA_INET6_ICMP6STATS, ICMP6_MIB_MAX * sizeof(u64));
+- if (nla == NULL)
+- goto nla_put_failure;
+- snmp6_fill_stats(nla_data(nla), idev, IFLA_INET6_ICMP6STATS, nla_len(nla));
+-
+- nla_nest_end(skb, protoinfo);
+- return nlmsg_end(skb, nlh);
+-
+-nla_put_failure:
+- nlmsg_cancel(skb, nlh);
+- return -EMSGSIZE;
+-}
+-
+-static int inet6_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
+-{
+- int idx, err;
+- int s_idx = cb->args[0];
+- struct net_device *dev;
+- struct inet6_dev *idev;
+-
+- read_lock(&dev_base_lock);
+- idx = 0;
+- for_each_netdev(dev) {
+- if (idx < s_idx)
+- goto cont;
+- if ((idev = in6_dev_get(dev)) == NULL)
+- goto cont;
+- err = inet6_fill_ifinfo(skb, idev, NETLINK_CB(cb->skb).pid,
+- cb->nlh->nlmsg_seq, RTM_NEWLINK, NLM_F_MULTI);
+- in6_dev_put(idev);
+- if (err <= 0)
+- break;
+-cont:
+- idx++;
+- }
+- read_unlock(&dev_base_lock);
+- cb->args[0] = idx;
+-
+- return skb->len;
+-}
+-
+-void inet6_ifinfo_notify(int event, struct inet6_dev *idev)
+-{
+- struct sk_buff *skb;
+- int err = -ENOBUFS;
+-
+- skb = nlmsg_new(inet6_if_nlmsg_size(), GFP_ATOMIC);
+- if (skb == NULL)
+- goto errout;
+-
+- err = inet6_fill_ifinfo(skb, idev, 0, 0, event, 0);
+- if (err < 0) {
+- /* -EMSGSIZE implies BUG in inet6_if_nlmsg_size() */
+- WARN_ON(err == -EMSGSIZE);
+- kfree_skb(skb);
+- goto errout;
+- }
+- err = rtnl_notify(skb, 0, RTNLGRP_IPV6_IFADDR, NULL, GFP_ATOMIC);
+-errout:
+- if (err < 0)
+- rtnl_set_sk_err(RTNLGRP_IPV6_IFADDR, err);
+-}
+-
+-static inline size_t inet6_prefix_nlmsg_size(void)
+-{
+- return NLMSG_ALIGN(sizeof(struct prefixmsg))
+- + nla_total_size(sizeof(struct in6_addr))
+- + nla_total_size(sizeof(struct prefix_cacheinfo));
+-}
+-
+-static int inet6_fill_prefix(struct sk_buff *skb, struct inet6_dev *idev,
+- struct prefix_info *pinfo, u32 pid, u32 seq,
+- int event, unsigned int flags)
+-{
+- struct prefixmsg *pmsg;
+- struct nlmsghdr *nlh;
+- struct prefix_cacheinfo ci;
+-
+- nlh = nlmsg_put(skb, pid, seq, event, sizeof(*pmsg), flags);
+- if (nlh == NULL)
+- return -EMSGSIZE;
+-
+- pmsg = nlmsg_data(nlh);
+- pmsg->prefix_family = AF_INET6;
+- pmsg->prefix_pad1 = 0;
+- pmsg->prefix_pad2 = 0;
+- pmsg->prefix_ifindex = idev->dev->ifindex;
+- pmsg->prefix_len = pinfo->prefix_len;
+- pmsg->prefix_type = pinfo->type;
+- pmsg->prefix_pad3 = 0;
+- pmsg->prefix_flags = 0;
+- if (pinfo->onlink)
+- pmsg->prefix_flags |= IF_PREFIX_ONLINK;
+- if (pinfo->autoconf)
+- pmsg->prefix_flags |= IF_PREFIX_AUTOCONF;
+-
+- NLA_PUT(skb, PREFIX_ADDRESS, sizeof(pinfo->prefix), &pinfo->prefix);
+-
+- ci.preferred_time = ntohl(pinfo->prefered);
+- ci.valid_time = ntohl(pinfo->valid);
+- NLA_PUT(skb, PREFIX_CACHEINFO, sizeof(ci), &ci);
+-
+- return nlmsg_end(skb, nlh);
+-
+-nla_put_failure:
+- nlmsg_cancel(skb, nlh);
+- return -EMSGSIZE;
+-}
+-
+-static void inet6_prefix_notify(int event, struct inet6_dev *idev,
+- struct prefix_info *pinfo)
+-{
+- struct sk_buff *skb;
+- int err = -ENOBUFS;
+-
+- skb = nlmsg_new(inet6_prefix_nlmsg_size(), GFP_ATOMIC);
+- if (skb == NULL)
+- goto errout;
+-
+- err = inet6_fill_prefix(skb, idev, pinfo, 0, 0, event, 0);
+- if (err < 0) {
+- /* -EMSGSIZE implies BUG in inet6_prefix_nlmsg_size() */
+- WARN_ON(err == -EMSGSIZE);
+- kfree_skb(skb);
+- goto errout;
+- }
+- err = rtnl_notify(skb, 0, RTNLGRP_IPV6_PREFIX, NULL, GFP_ATOMIC);
+-errout:
+- if (err < 0)
+- rtnl_set_sk_err(RTNLGRP_IPV6_PREFIX, err);
+-}
+-
+-static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)
+-{
+- inet6_ifa_notify(event ? : RTM_NEWADDR, ifp);
+-
+- switch (event) {
+- case RTM_NEWADDR:
+- /*
+- * If the address was optimistic
+- * we inserted the route at the start of
+- * our DAD process, so we don't need
+- * to do it again
+- */
+- if (!(ifp->rt->rt6i_node))
+- ip6_ins_rt(ifp->rt);
+- if (ifp->idev->cnf.forwarding)
+- addrconf_join_anycast(ifp);
+- break;
+- case RTM_DELADDR:
+- if (ifp->idev->cnf.forwarding)
+- addrconf_leave_anycast(ifp);
+- addrconf_leave_solict(ifp->idev, &ifp->addr);
+- dst_hold(&ifp->rt->u.dst);
+- if (ip6_del_rt(ifp->rt))
+- dst_free(&ifp->rt->u.dst);
+- break;
+- }
+-}
+-
+-static void ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)
+-{
+- rcu_read_lock_bh();
+- if (likely(ifp->idev->dead == 0))
+- __ipv6_ifa_notify(event, ifp);
+- rcu_read_unlock_bh();
+-}
+-
+-#ifdef CONFIG_SYSCTL
+-
+-static
+-int addrconf_sysctl_forward(ctl_table *ctl, int write, struct file * filp,
+- void __user *buffer, size_t *lenp, loff_t *ppos)
+-{
+- int *valp = ctl->data;
+- int val = *valp;
+- int ret;
+-
+- ret = proc_dointvec(ctl, write, filp, buffer, lenp, ppos);
+-
+- if (write && valp != &ipv6_devconf_dflt.forwarding) {
+- if (valp != &ipv6_devconf.forwarding) {
+- if ((!*valp) ^ (!val)) {
+- struct inet6_dev *idev = (struct inet6_dev *)ctl->extra1;
+- if (idev == NULL)
+- return ret;
+- dev_forward_change(idev);
+- }
+- } else {
+- ipv6_devconf_dflt.forwarding = ipv6_devconf.forwarding;
+- addrconf_forward_change();
+- }
+- if (*valp)
+- rt6_purge_dflt_routers();
+- }
+-
+- return ret;
+-}
+-
+-static int addrconf_sysctl_forward_strategy(ctl_table *table,
+- int __user *name, int nlen,
+- void __user *oldval,
+- size_t __user *oldlenp,
+- void __user *newval, size_t newlen)
+-{
+- int *valp = table->data;
+- int new;
+-
+- if (!newval || !newlen)
+- return 0;
+- if (newlen != sizeof(int))
+- return -EINVAL;
+- if (get_user(new, (int __user *)newval))
+- return -EFAULT;
+- if (new == *valp)
+- return 0;
+- if (oldval && oldlenp) {
+- size_t len;
+- if (get_user(len, oldlenp))
+- return -EFAULT;
+- if (len) {
+- if (len > table->maxlen)
+- len = table->maxlen;
+- if (copy_to_user(oldval, valp, len))
+- return -EFAULT;
+- if (put_user(len, oldlenp))
+- return -EFAULT;
+- }
+- }
+-
+- if (valp != &ipv6_devconf_dflt.forwarding) {
+- if (valp != &ipv6_devconf.forwarding) {
+- struct inet6_dev *idev = (struct inet6_dev *)table->extra1;
+- int changed;
+- if (unlikely(idev == NULL))
+- return -ENODEV;
+- changed = (!*valp) ^ (!new);
+- *valp = new;
+- if (changed)
+- dev_forward_change(idev);
+- } else {
+- *valp = new;
+- addrconf_forward_change();
+- }
+-
+- if (*valp)
+- rt6_purge_dflt_routers();
+- } else
+- *valp = new;
+-
+- return 1;
+-}
+-
+-static struct addrconf_sysctl_table
+-{
+- struct ctl_table_header *sysctl_header;
+- ctl_table addrconf_vars[__NET_IPV6_MAX];
+- ctl_table addrconf_dev[2];
+- ctl_table addrconf_conf_dir[2];
+- ctl_table addrconf_proto_dir[2];
+- ctl_table addrconf_root_dir[2];
+-} addrconf_sysctl __read_mostly = {
+- .sysctl_header = NULL,
+- .addrconf_vars = {
+- {
+- .ctl_name = NET_IPV6_FORWARDING,
+- .procname = "forwarding",
+- .data = &ipv6_devconf.forwarding,
+- .maxlen = sizeof(int),
+- .mode = 0644,
+- .proc_handler = &addrconf_sysctl_forward,
+- .strategy = &addrconf_sysctl_forward_strategy,
+- },
+- {
+- .ctl_name = NET_IPV6_HOP_LIMIT,
+- .procname = "hop_limit",
+- .data = &ipv6_devconf.hop_limit,
+- .maxlen = sizeof(int),
+- .mode = 0644,
+- .proc_handler = proc_dointvec,
+- },
+- {
+- .ctl_name = NET_IPV6_MTU,
+- .procname = "mtu",
+- .data = &ipv6_devconf.mtu6,
+- .maxlen = sizeof(int),
+- .mode = 0644,
+- .proc_handler = &proc_dointvec,
+- },
+- {
+- .ctl_name = NET_IPV6_ACCEPT_RA,
+- .procname = "accept_ra",
+- .data = &ipv6_devconf.accept_ra,
+- .maxlen = sizeof(int),
+- .mode = 0644,
+- .proc_handler = &proc_dointvec,
+- },
+- {
+- .ctl_name = NET_IPV6_ACCEPT_REDIRECTS,
+- .procname = "accept_redirects",
+- .data = &ipv6_devconf.accept_redirects,
+- .maxlen = sizeof(int),
+- .mode = 0644,
+- .proc_handler = &proc_dointvec,
+- },
+- {
+- .ctl_name = NET_IPV6_AUTOCONF,
+- .procname = "autoconf",
+- .data = &ipv6_devconf.autoconf,
+- .maxlen = sizeof(int),
+- .mode = 0644,
+- .proc_handler = &proc_dointvec,
+- },
+- {
+- .ctl_name = NET_IPV6_DAD_TRANSMITS,
+- .procname = "dad_transmits",
+- .data = &ipv6_devconf.dad_transmits,
+- .maxlen = sizeof(int),
+- .mode = 0644,
+- .proc_handler = &proc_dointvec,
+- },
+- {
+- .ctl_name = NET_IPV6_RTR_SOLICITS,
+- .procname = "router_solicitations",
+- .data = &ipv6_devconf.rtr_solicits,
+- .maxlen = sizeof(int),
+- .mode = 0644,
+- .proc_handler = &proc_dointvec,
+- },
+- {
+- .ctl_name = NET_IPV6_RTR_SOLICIT_INTERVAL,
+- .procname = "router_solicitation_interval",
+- .data = &ipv6_devconf.rtr_solicit_interval,
+- .maxlen = sizeof(int),
+- .mode = 0644,
+- .proc_handler = &proc_dointvec_jiffies,
+- .strategy = &sysctl_jiffies,
+- },
+- {
+- .ctl_name = NET_IPV6_RTR_SOLICIT_DELAY,
+- .procname = "router_solicitation_delay",
+- .data = &ipv6_devconf.rtr_solicit_delay,
+- .maxlen = sizeof(int),
+- .mode = 0644,
+- .proc_handler = &proc_dointvec_jiffies,
+- .strategy = &sysctl_jiffies,
+- },
+- {
+- .ctl_name = NET_IPV6_FORCE_MLD_VERSION,
+- .procname = "force_mld_version",
+- .data = &ipv6_devconf.force_mld_version,
+- .maxlen = sizeof(int),
+- .mode = 0644,
+- .proc_handler = &proc_dointvec,
+- },
+-#ifdef CONFIG_IPV6_PRIVACY
+- {
+- .ctl_name = NET_IPV6_USE_TEMPADDR,
+- .procname = "use_tempaddr",
+- .data = &ipv6_devconf.use_tempaddr,
+- .maxlen = sizeof(int),
+- .mode = 0644,
+- .proc_handler = &proc_dointvec,
+- },
+- {
+- .ctl_name = NET_IPV6_TEMP_VALID_LFT,
+- .procname = "temp_valid_lft",
+- .data = &ipv6_devconf.temp_valid_lft,
+- .maxlen = sizeof(int),
+- .mode = 0644,
+- .proc_handler = &proc_dointvec,
+- },
+- {
+- .ctl_name = NET_IPV6_TEMP_PREFERED_LFT,
+- .procname = "temp_prefered_lft",
+- .data = &ipv6_devconf.temp_prefered_lft,
+- .maxlen = sizeof(int),
+- .mode = 0644,
+- .proc_handler = &proc_dointvec,
+- },
+- {
+- .ctl_name = NET_IPV6_REGEN_MAX_RETRY,
+- .procname = "regen_max_retry",
+- .data = &ipv6_devconf.regen_max_retry,
+- .maxlen = sizeof(int),
+- .mode = 0644,
+- .proc_handler = &proc_dointvec,
+- },
+- {
+- .ctl_name = NET_IPV6_MAX_DESYNC_FACTOR,
+- .procname = "max_desync_factor",
+- .data = &ipv6_devconf.max_desync_factor,
+- .maxlen = sizeof(int),
+- .mode = 0644,
+- .proc_handler = &proc_dointvec,
+- },
+-#endif
+- {
+- .ctl_name = NET_IPV6_MAX_ADDRESSES,
+- .procname = "max_addresses",
+- .data = &ipv6_devconf.max_addresses,
+- .maxlen = sizeof(int),
+- .mode = 0644,
+- .proc_handler = &proc_dointvec,
+- },
+- {
+- .ctl_name = NET_IPV6_ACCEPT_RA_DEFRTR,
+- .procname = "accept_ra_defrtr",
+- .data = &ipv6_devconf.accept_ra_defrtr,
+- .maxlen = sizeof(int),
+- .mode = 0644,
+- .proc_handler = &proc_dointvec,
+- },
+- {
+- .ctl_name = NET_IPV6_ACCEPT_RA_PINFO,
+- .procname = "accept_ra_pinfo",
+- .data = &ipv6_devconf.accept_ra_pinfo,
+- .maxlen = sizeof(int),
+- .mode = 0644,
+- .proc_handler = &proc_dointvec,
+- },
+-#ifdef CONFIG_IPV6_ROUTER_PREF
+- {
+- .ctl_name = NET_IPV6_ACCEPT_RA_RTR_PREF,
+- .procname = "accept_ra_rtr_pref",
+- .data = &ipv6_devconf.accept_ra_rtr_pref,
+- .maxlen = sizeof(int),
+- .mode = 0644,
+- .proc_handler = &proc_dointvec,
+- },
+- {
+- .ctl_name = NET_IPV6_RTR_PROBE_INTERVAL,
+- .procname = "router_probe_interval",
+- .data = &ipv6_devconf.rtr_probe_interval,
+- .maxlen = sizeof(int),
+- .mode = 0644,
+- .proc_handler = &proc_dointvec_jiffies,
+- .strategy = &sysctl_jiffies,
+- },
+-#ifdef CONFIG_IPV6_ROUTE_INFO
+- {
+- .ctl_name = NET_IPV6_ACCEPT_RA_RT_INFO_MAX_PLEN,
+- .procname = "accept_ra_rt_info_max_plen",
+- .data = &ipv6_devconf.accept_ra_rt_info_max_plen,
+- .maxlen = sizeof(int),
+- .mode = 0644,
+- .proc_handler = &proc_dointvec,
+- },
+-#endif
+-#endif
+- {
+- .ctl_name = NET_IPV6_PROXY_NDP,
+- .procname = "proxy_ndp",
+- .data = &ipv6_devconf.proxy_ndp,
+- .maxlen = sizeof(int),
+- .mode = 0644,
+- .proc_handler = &proc_dointvec,
+- },
+- {
+- .ctl_name = NET_IPV6_ACCEPT_SOURCE_ROUTE,
+- .procname = "accept_source_route",
+- .data = &ipv6_devconf.accept_source_route,
+- .maxlen = sizeof(int),
+- .mode = 0644,
+- .proc_handler = &proc_dointvec,
+- },
+-#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
+- {
+- .ctl_name = CTL_UNNUMBERED,
+- .procname = "optimistic_dad",
+- .data = &ipv6_devconf.optimistic_dad,
+- .maxlen = sizeof(int),
+- .mode = 0644,
+- .proc_handler = &proc_dointvec,
+-
+- },
+-#endif
+- {
+- .ctl_name = 0, /* sentinel */
+- }
+- },
+- .addrconf_dev = {
+- {
+- .ctl_name = NET_PROTO_CONF_ALL,
+- .procname = "all",
+- .mode = 0555,
+- .child = addrconf_sysctl.addrconf_vars,
+- },
+- {
+- .ctl_name = 0, /* sentinel */
+- }
+- },
+- .addrconf_conf_dir = {
+- {
+- .ctl_name = NET_IPV6_CONF,
+- .procname = "conf",
+- .mode = 0555,
+- .child = addrconf_sysctl.addrconf_dev,
+- },
+- {
+- .ctl_name = 0, /* sentinel */
+- }
+- },
+- .addrconf_proto_dir = {
+- {
+- .ctl_name = NET_IPV6,
+- .procname = "ipv6",
+- .mode = 0555,
+- .child = addrconf_sysctl.addrconf_conf_dir,
+- },
+- {
+- .ctl_name = 0, /* sentinel */
+- }
+- },
+- .addrconf_root_dir = {
+- {
+- .ctl_name = CTL_NET,
+- .procname = "net",
+- .mode = 0555,
+- .child = addrconf_sysctl.addrconf_proto_dir,
+- },
+- {
+- .ctl_name = 0, /* sentinel */
+- }
+- },
+-};
+-
+-static void addrconf_sysctl_register(struct inet6_dev *idev, struct ipv6_devconf *p)
+-{
+- int i;
+- struct net_device *dev = idev ? idev->dev : NULL;
+- struct addrconf_sysctl_table *t;
+- char *dev_name = NULL;
+-
+- t = kmemdup(&addrconf_sysctl, sizeof(*t), GFP_KERNEL);
+- if (t == NULL)
+- return;
+- for (i=0; t->addrconf_vars[i].data; i++) {
+- t->addrconf_vars[i].data += (char*)p - (char*)&ipv6_devconf;
+- t->addrconf_vars[i].extra1 = idev; /* embedded; no ref */
+- }
+- if (dev) {
+- dev_name = dev->name;
+- t->addrconf_dev[0].ctl_name = dev->ifindex;
+- } else {
+- dev_name = "default";
+- t->addrconf_dev[0].ctl_name = NET_PROTO_CONF_DEFAULT;
+- }
+-
+- /*
+- * Make a copy of dev_name, because '.procname' is regarded as const
+- * by sysctl and we wouldn't want anyone to change it under our feet
+- * (see SIOCSIFNAME).
+- */
+- dev_name = kstrdup(dev_name, GFP_KERNEL);
+- if (!dev_name)
+- goto free;
+-
+- t->addrconf_dev[0].procname = dev_name;
+-
+- t->addrconf_dev[0].child = t->addrconf_vars;
+- t->addrconf_conf_dir[0].child = t->addrconf_dev;
+- t->addrconf_proto_dir[0].child = t->addrconf_conf_dir;
+- t->addrconf_root_dir[0].child = t->addrconf_proto_dir;
+-
+- t->sysctl_header = register_sysctl_table(t->addrconf_root_dir);
+- if (t->sysctl_header == NULL)
+- goto free_procname;
+- else
+- p->sysctl = t;
+- return;
+-
+- /* error path */
+- free_procname:
+- kfree(dev_name);
+- free:
+- kfree(t);
+-
+- return;
+-}
+-
+-static void addrconf_sysctl_unregister(struct ipv6_devconf *p)
+-{
+- if (p->sysctl) {
+- struct addrconf_sysctl_table *t = p->sysctl;
+- p->sysctl = NULL;
+- unregister_sysctl_table(t->sysctl_header);
+- kfree(t->addrconf_dev[0].procname);
+- kfree(t);
+- }
+-}
+-
+-
+-#endif
+-
+-/*
+- * Device notifier
+- */
+-
+-int register_inet6addr_notifier(struct notifier_block *nb)
+-{
+- return atomic_notifier_chain_register(&inet6addr_chain, nb);
+-}
+-
+-EXPORT_SYMBOL(register_inet6addr_notifier);
+-
+-int unregister_inet6addr_notifier(struct notifier_block *nb)
+-{
+- return atomic_notifier_chain_unregister(&inet6addr_chain,nb);
+-}
+-
+-EXPORT_SYMBOL(unregister_inet6addr_notifier);
+-
+-/*
+- * Init / cleanup code
+- */
+-
+-int __init addrconf_init(void)
+-{
+- int err = 0;
+-
+- /* The addrconf netdev notifier requires that loopback_dev
+- * has it's ipv6 private information allocated and setup
+- * before it can bring up and give link-local addresses
+- * to other devices which are up.
+- *
+- * Unfortunately, loopback_dev is not necessarily the first
+- * entry in the global dev_base list of net devices. In fact,
+- * it is likely to be the very last entry on that list.
+- * So this causes the notifier registry below to try and
+- * give link-local addresses to all devices besides loopback_dev
+- * first, then loopback_dev, which cases all the non-loopback_dev
+- * devices to fail to get a link-local address.
+- *
+- * So, as a temporary fix, allocate the ipv6 structure for
+- * loopback_dev first by hand.
+- * Longer term, all of the dependencies ipv6 has upon the loopback
+- * device and it being up should be removed.
+- */
+- rtnl_lock();
+- if (!ipv6_add_dev(&loopback_dev))
+- err = -ENOMEM;
+- rtnl_unlock();
+- if (err)
+- return err;
+-
+- ip6_null_entry.rt6i_idev = in6_dev_get(&loopback_dev);
+-#ifdef CONFIG_IPV6_MULTIPLE_TABLES
+- ip6_prohibit_entry.rt6i_idev = in6_dev_get(&loopback_dev);
+- ip6_blk_hole_entry.rt6i_idev = in6_dev_get(&loopback_dev);
+-#endif
+-
+- register_netdevice_notifier(&ipv6_dev_notf);
+-
+- addrconf_verify(0);
+-
+- err = __rtnl_register(PF_INET6, RTM_GETLINK, NULL, inet6_dump_ifinfo);
+- if (err < 0)
+- goto errout;
+-
+- /* Only the first call to __rtnl_register can fail */
+- __rtnl_register(PF_INET6, RTM_NEWADDR, inet6_rtm_newaddr, NULL);
+- __rtnl_register(PF_INET6, RTM_DELADDR, inet6_rtm_deladdr, NULL);
+- __rtnl_register(PF_INET6, RTM_GETADDR, inet6_rtm_getaddr, inet6_dump_ifaddr);
+- __rtnl_register(PF_INET6, RTM_GETMULTICAST, NULL, inet6_dump_ifmcaddr);
+- __rtnl_register(PF_INET6, RTM_GETANYCAST, NULL, inet6_dump_ifacaddr);
+-
+-#ifdef CONFIG_SYSCTL
+- addrconf_sysctl.sysctl_header =
+- register_sysctl_table(addrconf_sysctl.addrconf_root_dir);
+- addrconf_sysctl_register(NULL, &ipv6_devconf_dflt);
+-#endif
+-
+- return 0;
+-errout:
+- unregister_netdevice_notifier(&ipv6_dev_notf);
+-
+- return err;
+-}
+-
+-void __exit addrconf_cleanup(void)
+-{
+- struct net_device *dev;
+- struct inet6_dev *idev;
+- struct inet6_ifaddr *ifa;
+- int i;
+-
+- unregister_netdevice_notifier(&ipv6_dev_notf);
+-
+-#ifdef CONFIG_SYSCTL
+- addrconf_sysctl_unregister(&ipv6_devconf_dflt);
+- addrconf_sysctl_unregister(&ipv6_devconf);
+-#endif
+-
+- rtnl_lock();
+-
+- /*
+- * clean dev list.
+- */
+-
+- for_each_netdev(dev) {
+- if ((idev = __in6_dev_get(dev)) == NULL)
+- continue;
+- addrconf_ifdown(dev, 1);
+- }
+- addrconf_ifdown(&loopback_dev, 2);
+-
+- /*
+- * Check hash table.
+- */
+-
+- write_lock_bh(&addrconf_hash_lock);
+- for (i=0; i < IN6_ADDR_HSIZE; i++) {
+- for (ifa=inet6_addr_lst[i]; ifa; ) {
+- struct inet6_ifaddr *bifa;
+-
+- bifa = ifa;
+- ifa = ifa->lst_next;
+- printk(KERN_DEBUG "bug: IPv6 address leakage detected: ifa=%p\n", bifa);
+- /* Do not free it; something is wrong.
+- Now we can investigate it with debugger.
+- */
+- }
+- }
+- write_unlock_bh(&addrconf_hash_lock);
+-
+- del_timer(&addr_chk_timer);
+-
+- rtnl_unlock();
+-
+-#ifdef CONFIG_PROC_FS
+- proc_net_remove("if_inet6");
+-#endif
+-}
+diff -Nurb linux-2.6.22-570/net/ipv6/af_inet6.c linux-2.6.22-590/net/ipv6/af_inet6.c
+--- linux-2.6.22-570/net/ipv6/af_inet6.c 2008-01-29 22:12:21.000000000 -0500
++++ linux-2.6.22-590/net/ipv6/af_inet6.c 2008-01-29 22:12:32.000000000 -0500
+@@ -59,9 +59,6 @@
+ #ifdef CONFIG_IPV6_TUNNEL
+ #include <net/ip6_tunnel.h>
+ #endif
+-#ifdef CONFIG_IPV6_MIP6
+-#include <net/mip6.h>
+-#endif
+
+ #include <asm/uaccess.h>
+ #include <asm/system.h>
+@@ -85,7 +82,7 @@
+ return (struct ipv6_pinfo *)(((u8 *)sk) + offset);
+ }
+
+-static int inet6_create(struct socket *sock, int protocol)
++static int inet6_create(struct net *net, struct socket *sock, int protocol)
+ {
+ struct inet_sock *inet;
+ struct ipv6_pinfo *np;
+@@ -98,6 +95,9 @@
+ int try_loading_module = 0;
+ int err;
+
++ if (net != &init_net)
++ return -EAFNOSUPPORT;
++
+ if (sock->type != SOCK_RAW &&
+ sock->type != SOCK_DGRAM &&
+ !inet_ehash_secret)
+@@ -166,7 +166,7 @@
+ BUG_TRAP(answer_prot->slab != NULL);
+
+ err = -ENOBUFS;
+- sk = sk_alloc(PF_INET6, GFP_KERNEL, answer_prot, 1);
++ sk = sk_alloc(net, PF_INET6, GFP_KERNEL, answer_prot, 1);
+ if (sk == NULL)
+ goto out;
+
+@@ -209,7 +209,7 @@
+ inet->mc_index = 0;
+ inet->mc_list = NULL;
+
+- if (ipv4_config.no_pmtu_disc)
++ if (init_net.sysctl_ipv4_no_pmtu_disc)
+ inet->pmtudisc = IP_PMTUDISC_DONT;
+ else
+ inet->pmtudisc = IP_PMTUDISC_WANT;
+@@ -290,7 +290,7 @@
+ /* Check if the address belongs to the host. */
+ if (addr_type == IPV6_ADDR_MAPPED) {
+ v4addr = addr->sin6_addr.s6_addr32[3];
+- if (inet_addr_type(v4addr) != RTN_LOCAL) {
++ if (inet_addr_type(&init_net, v4addr) != RTN_LOCAL) {
+ err = -EADDRNOTAVAIL;
+ goto out;
+ }
+@@ -316,7 +316,7 @@
+ err = -EINVAL;
+ goto out;
+ }
+- dev = dev_get_by_index(sk->sk_bound_dev_if);
++ dev = dev_get_by_index(&init_net, sk->sk_bound_dev_if);
+ if (!dev) {
+ err = -ENODEV;
+ goto out;
+@@ -675,6 +675,7 @@
+ struct flowi fl;
+
+ memset(&fl, 0, sizeof(fl));
++ fl.fl_net = &init_net;
+ fl.proto = sk->sk_protocol;
+ ipv6_addr_copy(&fl.fl6_dst, &np->daddr);
+ ipv6_addr_copy(&fl.fl6_src, &np->saddr);
+@@ -876,9 +877,6 @@
+ ipv6_frag_init();
+ ipv6_nodata_init();
+ ipv6_destopt_init();
+-#ifdef CONFIG_IPV6_MIP6
+- mip6_init();
+-#endif
+
+ /* Init v6 transport protocols. */
+ udpv6_init();
+@@ -944,9 +942,7 @@
+
+ /* Cleanup code parts. */
+ ipv6_packet_cleanup();
+-#ifdef CONFIG_IPV6_MIP6
+- mip6_fini();
+-#endif
++
+ addrconf_cleanup();
+ ip6_flowlabel_cleanup();
+ ip6_route_cleanup();
+diff -Nurb linux-2.6.22-570/net/ipv6/ah6.c linux-2.6.22-590/net/ipv6/ah6.c
+--- linux-2.6.22-570/net/ipv6/ah6.c 2007-07-08 19:32:17.000000000 -0400
++++ linux-2.6.22-590/net/ipv6/ah6.c 2008-01-29 22:12:32.000000000 -0500
+@@ -74,7 +74,7 @@
+ return 0;
+ }
+
+-#ifdef CONFIG_IPV6_MIP6
++#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
+ /**
+ * ipv6_rearrange_destopt - rearrange IPv6 destination options header
+ * @iph: IPv6 header
+@@ -132,6 +132,8 @@
+ bad:
+ return;
+ }
++#else
++static void ipv6_rearrange_destopt(struct ipv6hdr *iph, struct ipv6_opt_hdr *destopt) {}
+ #endif
+
+ /**
+@@ -189,10 +191,8 @@
+ while (exthdr.raw < end) {
+ switch (nexthdr) {
+ case NEXTHDR_DEST:
+-#ifdef CONFIG_IPV6_MIP6
+ if (dir == XFRM_POLICY_OUT)
+ ipv6_rearrange_destopt(iph, exthdr.opth);
+-#endif
+ case NEXTHDR_HOP:
+ if (!zero_out_mutable_opts(exthdr.opth)) {
+ LIMIT_NETDEBUG(
+@@ -228,7 +228,7 @@
+ u8 nexthdr;
+ char tmp_base[8];
+ struct {
+-#ifdef CONFIG_IPV6_MIP6
++#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
+ struct in6_addr saddr;
+ #endif
+ struct in6_addr daddr;
+@@ -255,7 +255,7 @@
+ err = -ENOMEM;
+ goto error;
+ }
+-#ifdef CONFIG_IPV6_MIP6
++#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
+ memcpy(tmp_ext, &top_iph->saddr, extlen);
+ #else
+ memcpy(tmp_ext, &top_iph->daddr, extlen);
+@@ -294,7 +294,7 @@
+
+ memcpy(top_iph, tmp_base, sizeof(tmp_base));
+ if (tmp_ext) {
+-#ifdef CONFIG_IPV6_MIP6
++#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
+ memcpy(&top_iph->saddr, tmp_ext, extlen);
+ #else
+ memcpy(&top_iph->daddr, tmp_ext, extlen);
+@@ -554,3 +554,4 @@
+ module_exit(ah6_fini);
+
+ MODULE_LICENSE("GPL");
++MODULE_ALIAS_XFRM_TYPE(AF_INET6, XFRM_PROTO_AH);
+diff -Nurb linux-2.6.22-570/net/ipv6/anycast.c linux-2.6.22-590/net/ipv6/anycast.c
+--- linux-2.6.22-570/net/ipv6/anycast.c 2008-01-29 22:12:18.000000000 -0500
++++ linux-2.6.22-590/net/ipv6/anycast.c 2008-01-29 22:12:32.000000000 -0500
+@@ -32,6 +32,7 @@
+
+ #include <net/sock.h>
+ #include <net/snmp.h>
++#include <net/net_namespace.h>
+
+ #include <net/ipv6.h>
+ #include <net/protocol.h>
+@@ -112,10 +113,10 @@
+ } else {
+ /* router, no matching interface: just pick one */
+
+- dev = dev_get_by_flags(IFF_UP, IFF_UP|IFF_LOOPBACK);
++ dev = dev_get_by_flags(&init_net, IFF_UP, IFF_UP|IFF_LOOPBACK);
+ }
+ } else
+- dev = dev_get_by_index(ifindex);
++ dev = dev_get_by_index(&init_net, ifindex);
+
+ if (dev == NULL) {
+ err = -ENODEV;
+@@ -196,7 +197,7 @@
+
+ write_unlock_bh(&ipv6_sk_ac_lock);
+
+- dev = dev_get_by_index(pac->acl_ifindex);
++ dev = dev_get_by_index(&init_net, pac->acl_ifindex);
+ if (dev) {
+ ipv6_dev_ac_dec(dev, &pac->acl_addr);
+ dev_put(dev);
+@@ -224,7 +225,7 @@
+ if (pac->acl_ifindex != prev_index) {
+ if (dev)
+ dev_put(dev);
+- dev = dev_get_by_index(pac->acl_ifindex);
++ dev = dev_get_by_index(&init_net, pac->acl_ifindex);
+ prev_index = pac->acl_ifindex;
+ }
+ if (dev)
+@@ -429,7 +430,7 @@
+ if (dev)
+ return ipv6_chk_acast_dev(dev, addr);
+ read_lock(&dev_base_lock);
+- for_each_netdev(dev)
++ for_each_netdev(&init_net, dev)
+ if (ipv6_chk_acast_dev(dev, addr)) {
+ found = 1;
+ break;
+@@ -453,7 +454,7 @@
+ struct ac6_iter_state *state = ac6_seq_private(seq);
+
+ state->idev = NULL;
+- for_each_netdev(state->dev) {
++ for_each_netdev(&init_net, state->dev) {
+ struct inet6_dev *idev;
+ idev = in6_dev_get(state->dev);
+ if (!idev)
+@@ -579,7 +580,7 @@
+
+ int __init ac6_proc_init(void)
+ {
+- if (!proc_net_fops_create("anycast6", S_IRUGO, &ac6_seq_fops))
++ if (!proc_net_fops_create(&init_net, "anycast6", S_IRUGO, &ac6_seq_fops))
+ return -ENOMEM;
+
+ return 0;
+@@ -587,7 +588,7 @@
+
+ void ac6_proc_exit(void)
+ {
+- proc_net_remove("anycast6");
++ proc_net_remove(&init_net, "anycast6");
+ }
+ #endif
+
+diff -Nurb linux-2.6.22-570/net/ipv6/datagram.c linux-2.6.22-590/net/ipv6/datagram.c
+--- linux-2.6.22-570/net/ipv6/datagram.c 2007-07-08 19:32:17.000000000 -0400
++++ linux-2.6.22-590/net/ipv6/datagram.c 2008-01-29 22:12:32.000000000 -0500
+@@ -60,6 +60,7 @@
+ return -EAFNOSUPPORT;
+
+ memset(&fl, 0, sizeof(fl));
++ fl.fl_net = &init_net;
+ if (np->sndflow) {
+ fl.fl6_flowlabel = usin->sin6_flowinfo&IPV6_FLOWINFO_MASK;
+ if (fl.fl6_flowlabel&IPV6_FLOWLABEL_MASK) {
+@@ -544,7 +545,7 @@
+ if (!src_info->ipi6_ifindex)
+ return -EINVAL;
+ else {
+- dev = dev_get_by_index(src_info->ipi6_ifindex);
++ dev = dev_get_by_index(&init_net, src_info->ipi6_ifindex);
+ if (!dev)
+ return -ENODEV;
+ }
+@@ -658,7 +659,7 @@
+
+ switch (rthdr->type) {
+ case IPV6_SRCRT_TYPE_0:
+-#ifdef CONFIG_IPV6_MIP6
++#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
+ case IPV6_SRCRT_TYPE_2:
+ #endif
+ break;
+diff -Nurb linux-2.6.22-570/net/ipv6/esp6.c linux-2.6.22-590/net/ipv6/esp6.c
+--- linux-2.6.22-570/net/ipv6/esp6.c 2007-07-08 19:32:17.000000000 -0400
++++ linux-2.6.22-590/net/ipv6/esp6.c 2008-01-29 22:12:32.000000000 -0500
+@@ -421,3 +421,4 @@
+ module_exit(esp6_fini);
+
+ MODULE_LICENSE("GPL");
++MODULE_ALIAS_XFRM_TYPE(AF_INET6, XFRM_PROTO_ESP);
+diff -Nurb linux-2.6.22-570/net/ipv6/exthdrs.c linux-2.6.22-590/net/ipv6/exthdrs.c
+--- linux-2.6.22-570/net/ipv6/exthdrs.c 2007-07-08 19:32:17.000000000 -0400
++++ linux-2.6.22-590/net/ipv6/exthdrs.c 2008-01-29 22:12:32.000000000 -0500
+@@ -42,7 +42,7 @@
+ #include <net/ndisc.h>
+ #include <net/ip6_route.h>
+ #include <net/addrconf.h>
+-#ifdef CONFIG_IPV6_MIP6
++#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
+ #include <net/xfrm.h>
+ #endif
+
+@@ -90,6 +90,7 @@
+ bad:
+ return -1;
+ }
++EXPORT_SYMBOL_GPL(ipv6_find_tlv);
+
+ /*
+ * Parsing tlv encoded headers.
+@@ -196,7 +197,7 @@
+ Destination options header.
+ *****************************/
+
+-#ifdef CONFIG_IPV6_MIP6
++#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
+ static int ipv6_dest_hao(struct sk_buff **skbp, int optoff)
+ {
+ struct sk_buff *skb = *skbp;
+@@ -270,7 +271,7 @@
+ #endif
+
+ static struct tlvtype_proc tlvprocdestopt_lst[] = {
+-#ifdef CONFIG_IPV6_MIP6
++#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
+ {
+ .type = IPV6_TLV_HAO,
+ .func = ipv6_dest_hao,
+@@ -283,7 +284,7 @@
+ {
+ struct sk_buff *skb = *skbp;
+ struct inet6_skb_parm *opt = IP6CB(skb);
+-#ifdef CONFIG_IPV6_MIP6
++#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
+ __u16 dstbuf;
+ #endif
+ struct dst_entry *dst;
+@@ -298,7 +299,7 @@
+ }
+
+ opt->lastopt = opt->dst1 = skb_network_header_len(skb);
+-#ifdef CONFIG_IPV6_MIP6
++#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
+ dstbuf = opt->dst1;
+ #endif
+
+@@ -308,7 +309,7 @@
+ skb = *skbp;
+ skb->transport_header += (skb_transport_header(skb)[1] + 1) << 3;
+ opt = IP6CB(skb);
+-#ifdef CONFIG_IPV6_MIP6
++#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
+ opt->nhoff = dstbuf;
+ #else
+ opt->nhoff = opt->dst1;
+@@ -427,7 +428,7 @@
+ looped_back:
+ if (hdr->segments_left == 0) {
+ switch (hdr->type) {
+-#ifdef CONFIG_IPV6_MIP6
++#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
+ case IPV6_SRCRT_TYPE_2:
+ /* Silently discard type 2 header unless it was
+ * processed by own
+@@ -463,7 +464,7 @@
+ return -1;
+ }
+ break;
+-#ifdef CONFIG_IPV6_MIP6
++#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
+ case IPV6_SRCRT_TYPE_2:
+ /* Silently discard invalid RTH type 2 */
+ if (hdr->hdrlen != 2 || hdr->segments_left != 1) {
+@@ -520,7 +521,7 @@
+ addr += i - 1;
+
+ switch (hdr->type) {
+-#ifdef CONFIG_IPV6_MIP6
++#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
+ case IPV6_SRCRT_TYPE_2:
+ if (xfrm6_input_addr(skb, (xfrm_address_t *)addr,
+ (xfrm_address_t *)&ipv6_hdr(skb)->saddr,
+diff -Nurb linux-2.6.22-570/net/ipv6/fib6_rules.c linux-2.6.22-590/net/ipv6/fib6_rules.c
+--- linux-2.6.22-570/net/ipv6/fib6_rules.c 2008-01-29 22:12:21.000000000 -0500
++++ linux-2.6.22-590/net/ipv6/fib6_rules.c 2008-01-29 22:12:32.000000000 -0500
+@@ -244,7 +244,7 @@
+ return -ENOBUFS;
+ }
+
+-static u32 fib6_rule_default_pref(void)
++static u32 fib6_rule_default_pref(struct fib_rules_ops *ops)
+ {
+ return 0x3FFF;
+ }
+@@ -277,10 +277,10 @@
+ list_add_tail(&local_rule.common.list, &fib6_rules);
+ list_add_tail(&main_rule.common.list, &fib6_rules);
+
+- fib_rules_register(&fib6_rules_ops);
++ fib_rules_register(&init_net, &fib6_rules_ops);
+ }
+
+ void fib6_rules_cleanup(void)
+ {
+- fib_rules_unregister(&fib6_rules_ops);
++ fib_rules_unregister(&init_net, &fib6_rules_ops);
+ }
+diff -Nurb linux-2.6.22-570/net/ipv6/icmp.c linux-2.6.22-590/net/ipv6/icmp.c
+--- linux-2.6.22-570/net/ipv6/icmp.c 2008-01-29 22:12:18.000000000 -0500
++++ linux-2.6.22-590/net/ipv6/icmp.c 2008-01-29 22:12:32.000000000 -0500
+@@ -272,7 +272,7 @@
+ return 0;
+ }
+
+-#ifdef CONFIG_IPV6_MIP6
++#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
+ static void mip6_addr_swap(struct sk_buff *skb)
+ {
+ struct ipv6hdr *iph = ipv6_hdr(skb);
+@@ -377,6 +377,7 @@
+ mip6_addr_swap(skb);
+
+ memset(&fl, 0, sizeof(fl));
++ fl.fl_net = &init_net;
+ fl.proto = IPPROTO_ICMPV6;
+ ipv6_addr_copy(&fl.fl6_dst, &hdr->saddr);
+ if (saddr)
+@@ -495,6 +496,7 @@
+ tmp_hdr.icmp6_type = ICMPV6_ECHO_REPLY;
+
+ memset(&fl, 0, sizeof(fl));
++ fl.fl_net = &init_net;
+ fl.proto = IPPROTO_ICMPV6;
+ ipv6_addr_copy(&fl.fl6_dst, &ipv6_hdr(skb)->saddr);
+ if (saddr)
+diff -Nurb linux-2.6.22-570/net/ipv6/inet6_connection_sock.c linux-2.6.22-590/net/ipv6/inet6_connection_sock.c
+--- linux-2.6.22-570/net/ipv6/inet6_connection_sock.c 2007-07-08 19:32:17.000000000 -0400
++++ linux-2.6.22-590/net/ipv6/inet6_connection_sock.c 2008-01-29 22:12:32.000000000 -0500
+@@ -149,6 +149,7 @@
+ struct in6_addr *final_p = NULL, final;
+
+ memset(&fl, 0, sizeof(fl));
++ fl.fl_net = &init_net;
+ fl.proto = sk->sk_protocol;
+ ipv6_addr_copy(&fl.fl6_dst, &np->daddr);
+ ipv6_addr_copy(&fl.fl6_src, &np->saddr);
+diff -Nurb linux-2.6.22-570/net/ipv6/inet6_hashtables.c linux-2.6.22-590/net/ipv6/inet6_hashtables.c
+--- linux-2.6.22-570/net/ipv6/inet6_hashtables.c 2008-01-29 22:12:21.000000000 -0500
++++ linux-2.6.22-590/net/ipv6/inet6_hashtables.c 2008-01-29 22:12:32.000000000 -0500
+@@ -61,7 +61,7 @@
+ const __be16 sport,
+ const struct in6_addr *daddr,
+ const u16 hnum,
+- const int dif)
++ const int dif, struct net *net)
+ {
+ struct sock *sk;
+ const struct hlist_node *node;
+@@ -105,7 +105,7 @@
+
+ struct sock *inet6_lookup_listener(struct inet_hashinfo *hashinfo,
+ const struct in6_addr *daddr,
+- const unsigned short hnum, const int dif)
++ const unsigned short hnum, const int dif, struct net *net)
+ {
+ struct sock *sk;
+ const struct hlist_node *node;
+@@ -113,7 +113,7 @@
+ int score, hiscore = 0;
+
+ read_lock(&hashinfo->lhash_lock);
+- sk_for_each(sk, node, &hashinfo->listening_hash[inet_lhashfn(hnum)]) {
++ sk_for_each(sk, node, &hashinfo->listening_hash[inet_lhashfn(net, hnum)]) {
+ if (inet_sk(sk)->num == hnum && sk->sk_family == PF_INET6) {
+ const struct ipv6_pinfo *np = inet6_sk(sk);
+
+@@ -152,12 +152,12 @@
+ struct sock *inet6_lookup(struct inet_hashinfo *hashinfo,
+ const struct in6_addr *saddr, const __be16 sport,
+ const struct in6_addr *daddr, const __be16 dport,
+- const int dif)
++ const int dif, struct net *net)
+ {
+ struct sock *sk;
+
+ local_bh_disable();
+- sk = __inet6_lookup(hashinfo, saddr, sport, daddr, ntohs(dport), dif);
++ sk = __inet6_lookup(hashinfo, saddr, sport, daddr, ntohs(dport), dif, net);
+ local_bh_enable();
+
+ return sk;
+@@ -251,6 +251,7 @@
+ int inet6_hash_connect(struct inet_timewait_death_row *death_row,
+ struct sock *sk)
+ {
++ struct net *net = sk->sk_net;
+ struct inet_hashinfo *hinfo = death_row->hashinfo;
+ const unsigned short snum = inet_sk(sk)->num;
+ struct inet_bind_hashbucket *head;
+@@ -258,8 +259,8 @@
+ int ret;
+
+ if (snum == 0) {
+- const int low = sysctl_local_port_range[0];
+- const int high = sysctl_local_port_range[1];
++ const int low = sk->sk_net->sysctl_local_port_range[0];
++ const int high = sk->sk_net->sysctl_local_port_range[1];
+ const int range = high - low;
+ int i, port;
+ static u32 hint;
+@@ -270,7 +271,7 @@
+ local_bh_disable();
+ for (i = 1; i <= range; i++) {
+ port = low + (i + offset) % range;
+- head = &hinfo->bhash[inet_bhashfn(port, hinfo->bhash_size)];
++ head = &hinfo->bhash[inet_bhashfn(net, port, hinfo->bhash_size)];
+ spin_lock(&head->lock);
+
+ /* Does not bother with rcv_saddr checks,
+@@ -278,7 +279,7 @@
+ * unique enough.
+ */
+ inet_bind_bucket_for_each(tb, node, &head->chain) {
+- if (tb->port == port) {
++ if ((tb->port == port) && (tb->net == net)) {
+ BUG_TRAP(!hlist_empty(&tb->owners));
+ if (tb->fastreuse >= 0)
+ goto next_port;
+@@ -291,7 +292,7 @@
+ }
+
+ tb = inet_bind_bucket_create(hinfo->bind_bucket_cachep,
+- head, port);
++ head, net, port);
+ if (!tb) {
+ spin_unlock(&head->lock);
+ break;
+@@ -326,7 +327,7 @@
+ goto out;
+ }
+
+- head = &hinfo->bhash[inet_bhashfn(snum, hinfo->bhash_size)];
++ head = &hinfo->bhash[inet_bhashfn(net, snum, hinfo->bhash_size)];
+ tb = inet_csk(sk)->icsk_bind_hash;
+ spin_lock_bh(&head->lock);
+
+diff -Nurb linux-2.6.22-570/net/ipv6/ip6_fib.c linux-2.6.22-590/net/ipv6/ip6_fib.c
+--- linux-2.6.22-570/net/ipv6/ip6_fib.c 2007-07-08 19:32:17.000000000 -0400
++++ linux-2.6.22-590/net/ipv6/ip6_fib.c 2008-01-29 22:12:32.000000000 -0500
+@@ -361,6 +361,7 @@
+
+ static int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb)
+ {
++ struct net *net = skb->sk->sk_net;
+ unsigned int h, s_h;
+ unsigned int e = 0, s_e;
+ struct rt6_rtnl_dump_arg arg;
+@@ -369,6 +370,9 @@
+ struct hlist_node *node;
+ int res = 0;
+
++ if (net != &init_net)
++ return 0;
++
+ s_h = cb->args[0];
+ s_e = cb->args[1];
+
+@@ -1311,6 +1315,11 @@
+
+ static int fib6_clean_node(struct fib6_walker_t *w)
+ {
++ struct nl_info info = {
++ .nlh = NULL,
++ .pid = 0,
++ .net = &init_net,
++ };
+ int res;
+ struct rt6_info *rt;
+ struct fib6_cleaner_t *c = (struct fib6_cleaner_t*)w;
+@@ -1319,7 +1328,7 @@
+ res = c->func(rt, c->arg);
+ if (res < 0) {
+ w->leaf = rt;
+- res = fib6_del(rt, NULL);
++ res = fib6_del(rt, &info);
+ if (res) {
+ #if RT6_DEBUG >= 2
+ printk(KERN_DEBUG "fib6_clean_node: del failed: rt=%p@%p err=%d\n", rt, rt->rt6i_node, res);
+diff -Nurb linux-2.6.22-570/net/ipv6/ip6_flowlabel.c linux-2.6.22-590/net/ipv6/ip6_flowlabel.c
+--- linux-2.6.22-570/net/ipv6/ip6_flowlabel.c 2007-07-08 19:32:17.000000000 -0400
++++ linux-2.6.22-590/net/ipv6/ip6_flowlabel.c 2008-01-29 22:12:32.000000000 -0500
+@@ -22,6 +22,7 @@
+ #include <linux/seq_file.h>
+
+ #include <net/sock.h>
++#include <net/net_namespace.h>
+
+ #include <net/ipv6.h>
+ #include <net/ndisc.h>
+@@ -309,6 +310,7 @@
+
+ msg.msg_controllen = olen;
+ msg.msg_control = (void*)(fl->opt+1);
++ flowi.fl_net = &init_net;
+ flowi.oif = 0;
+
+ err = datagram_send_ctl(&msg, &flowi, fl->opt, &junk, &junk);
+@@ -690,7 +692,7 @@
+ void ip6_flowlabel_init(void)
+ {
+ #ifdef CONFIG_PROC_FS
+- proc_net_fops_create("ip6_flowlabel", S_IRUGO, &ip6fl_seq_fops);
++ proc_net_fops_create(&init_net, "ip6_flowlabel", S_IRUGO, &ip6fl_seq_fops);
+ #endif
+ }
+
+@@ -698,6 +700,6 @@
+ {
+ del_timer(&ip6_fl_gc_timer);
+ #ifdef CONFIG_PROC_FS
+- proc_net_remove("ip6_flowlabel");
++ proc_net_remove(&init_net, "ip6_flowlabel");
+ #endif
+ }
+diff -Nurb linux-2.6.22-570/net/ipv6/ip6_input.c linux-2.6.22-590/net/ipv6/ip6_input.c
+--- linux-2.6.22-570/net/ipv6/ip6_input.c 2007-07-08 19:32:17.000000000 -0400
++++ linux-2.6.22-590/net/ipv6/ip6_input.c 2008-01-29 22:12:32.000000000 -0500
+@@ -61,6 +61,11 @@
+ u32 pkt_len;
+ struct inet6_dev *idev;
+
++ if (dev->nd_net != &init_net) {
++ kfree_skb(skb);
++ return 0;
++ }
++
+ if (skb->pkt_type == PACKET_OTHERHOST) {
+ kfree_skb(skb);
+ return 0;
+diff -Nurb linux-2.6.22-570/net/ipv6/ip6_output.c linux-2.6.22-590/net/ipv6/ip6_output.c
+--- linux-2.6.22-570/net/ipv6/ip6_output.c 2008-01-29 22:12:21.000000000 -0500
++++ linux-2.6.22-590/net/ipv6/ip6_output.c 2008-01-29 22:12:32.000000000 -0500
+@@ -423,7 +423,7 @@
+
+ /* XXX: idev->cnf.proxy_ndp? */
+ if (ipv6_devconf.proxy_ndp &&
+- pneigh_lookup(&nd_tbl, &hdr->daddr, skb->dev, 0)) {
++ pneigh_lookup(&nd_tbl, &init_net, &hdr->daddr, skb->dev, 0)) {
+ int proxied = ip6_forward_proxy_check(skb);
+ if (proxied > 0)
+ return ip6_input(skb);
+@@ -543,7 +543,7 @@
+ found_rhdr = 1;
+ break;
+ case NEXTHDR_DEST:
+-#ifdef CONFIG_IPV6_MIP6
++#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
+ if (ipv6_find_tlv(skb, offset, IPV6_TLV_HAO) >= 0)
+ break;
+ #endif
+diff -Nurb linux-2.6.22-570/net/ipv6/ip6_tunnel.c linux-2.6.22-590/net/ipv6/ip6_tunnel.c
+--- linux-2.6.22-570/net/ipv6/ip6_tunnel.c 2008-01-29 22:12:18.000000000 -0500
++++ linux-2.6.22-590/net/ipv6/ip6_tunnel.c 2008-01-29 22:12:32.000000000 -0500
+@@ -235,7 +235,7 @@
+ int i;
+ for (i = 1; i < IP6_TNL_MAX; i++) {
+ sprintf(name, "ip6tnl%d", i);
+- if (__dev_get_by_name(name) == NULL)
++ if (__dev_get_by_name(&init_net, name) == NULL)
+ break;
+ }
+ if (i == IP6_TNL_MAX)
+@@ -651,7 +651,7 @@
+ struct net_device *ldev = NULL;
+
+ if (p->link)
+- ldev = dev_get_by_index(p->link);
++ ldev = dev_get_by_index(&init_net, p->link);
+
+ if ((ipv6_addr_is_multicast(&p->laddr) ||
+ likely(ipv6_chk_addr(&p->laddr, ldev, 0))) &&
+@@ -787,7 +787,7 @@
+ struct net_device *ldev = NULL;
+
+ if (p->link)
+- ldev = dev_get_by_index(p->link);
++ ldev = dev_get_by_index(&init_net, p->link);
+
+ if (unlikely(!ipv6_chk_addr(&p->laddr, ldev, 0)))
+ printk(KERN_WARNING
+diff -Nurb linux-2.6.22-570/net/ipv6/ipcomp6.c linux-2.6.22-590/net/ipv6/ipcomp6.c
+--- linux-2.6.22-570/net/ipv6/ipcomp6.c 2008-01-29 22:12:18.000000000 -0500
++++ linux-2.6.22-590/net/ipv6/ipcomp6.c 2008-01-29 22:12:32.000000000 -0500
+@@ -501,4 +501,4 @@
+ MODULE_DESCRIPTION("IP Payload Compression Protocol (IPComp) for IPv6 - RFC3173");
+ MODULE_AUTHOR("Mitsuru KANDA <mk@linux-ipv6.org>");
+
+-
++MODULE_ALIAS_XFRM_TYPE(AF_INET6, XFRM_PROTO_COMP);
+diff -Nurb linux-2.6.22-570/net/ipv6/ipv6_sockglue.c linux-2.6.22-590/net/ipv6/ipv6_sockglue.c
+--- linux-2.6.22-570/net/ipv6/ipv6_sockglue.c 2008-01-29 22:12:18.000000000 -0500
++++ linux-2.6.22-590/net/ipv6/ipv6_sockglue.c 2008-01-29 22:12:32.000000000 -0500
+@@ -123,7 +123,7 @@
+ struct ipv6hdr *ipv6h;
+ struct inet6_protocol *ops;
+
+- if (!(features & NETIF_F_HW_CSUM))
++ if (!(features & NETIF_F_V6_CSUM))
+ features &= ~NETIF_F_SG;
+
+ if (unlikely(skb_shinfo(skb)->gso_type &
+@@ -417,7 +417,7 @@
+ struct ipv6_rt_hdr *rthdr = opt->srcrt;
+ switch (rthdr->type) {
+ case IPV6_SRCRT_TYPE_0:
+-#ifdef CONFIG_IPV6_MIP6
++#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
+ case IPV6_SRCRT_TYPE_2:
+ #endif
+ break;
+@@ -463,6 +463,7 @@
+ struct flowi fl;
+ int junk;
+
++ fl.fl_net = &init_net;
+ fl.fl6_flowlabel = 0;
+ fl.oif = sk->sk_bound_dev_if;
+
+@@ -547,7 +548,7 @@
+ if (sk->sk_bound_dev_if && sk->sk_bound_dev_if != val)
+ goto e_inval;
+
+- if (__dev_get_by_index(val) == NULL) {
++ if (__dev_get_by_index(&init_net, val) == NULL) {
+ retv = -ENODEV;
+ break;
+ }
+diff -Nurb linux-2.6.22-570/net/ipv6/mcast.c linux-2.6.22-590/net/ipv6/mcast.c
+--- linux-2.6.22-570/net/ipv6/mcast.c 2007-07-08 19:32:17.000000000 -0400
++++ linux-2.6.22-590/net/ipv6/mcast.c 2008-01-29 22:12:32.000000000 -0500
+@@ -51,6 +51,7 @@
+
+ #include <net/sock.h>
+ #include <net/snmp.h>
++#include <net/net_namespace.h>
+
+ #include <net/ipv6.h>
+ #include <net/protocol.h>
+@@ -214,7 +215,7 @@
+ dst_release(&rt->u.dst);
+ }
+ } else
+- dev = dev_get_by_index(ifindex);
++ dev = dev_get_by_index(&init_net, ifindex);
+
+ if (dev == NULL) {
+ sock_kfree_s(sk, mc_lst, sizeof(*mc_lst));
+@@ -265,7 +266,7 @@
+ *lnk = mc_lst->next;
+ write_unlock_bh(&ipv6_sk_mc_lock);
+
+- if ((dev = dev_get_by_index(mc_lst->ifindex)) != NULL) {
++ if ((dev = dev_get_by_index(&init_net, mc_lst->ifindex)) != NULL) {
+ struct inet6_dev *idev = in6_dev_get(dev);
+
+ (void) ip6_mc_leave_src(sk, mc_lst, idev);
+@@ -300,7 +301,7 @@
+ dst_release(&rt->u.dst);
+ }
+ } else
+- dev = dev_get_by_index(ifindex);
++ dev = dev_get_by_index(&init_net, ifindex);
+
+ if (!dev)
+ return NULL;
+@@ -331,7 +332,7 @@
+ np->ipv6_mc_list = mc_lst->next;
+ write_unlock_bh(&ipv6_sk_mc_lock);
+
+- dev = dev_get_by_index(mc_lst->ifindex);
++ dev = dev_get_by_index(&init_net, mc_lst->ifindex);
+ if (dev) {
+ struct inet6_dev *idev = in6_dev_get(dev);
+
+@@ -2332,7 +2333,7 @@
+ struct igmp6_mc_iter_state *state = igmp6_mc_seq_private(seq);
+
+ state->idev = NULL;
+- for_each_netdev(state->dev) {
++ for_each_netdev(&init_net, state->dev) {
+ struct inet6_dev *idev;
+ idev = in6_dev_get(state->dev);
+ if (!idev)
+@@ -2476,7 +2477,7 @@
+
+ state->idev = NULL;
+ state->im = NULL;
+- for_each_netdev(state->dev) {
++ for_each_netdev(&init_net, state->dev) {
+ struct inet6_dev *idev;
+ idev = in6_dev_get(state->dev);
+ if (unlikely(idev == NULL))
+@@ -2658,8 +2659,8 @@
+ np->hop_limit = 1;
+
+ #ifdef CONFIG_PROC_FS
+- proc_net_fops_create("igmp6", S_IRUGO, &igmp6_mc_seq_fops);
+- proc_net_fops_create("mcfilter6", S_IRUGO, &igmp6_mcf_seq_fops);
++ proc_net_fops_create(&init_net, "igmp6", S_IRUGO, &igmp6_mc_seq_fops);
++ proc_net_fops_create(&init_net, "mcfilter6", S_IRUGO, &igmp6_mcf_seq_fops);
+ #endif
+
+ return 0;
+@@ -2671,7 +2672,7 @@
+ igmp6_socket = NULL; /* for safety */
+
+ #ifdef CONFIG_PROC_FS
+- proc_net_remove("mcfilter6");
+- proc_net_remove("igmp6");
++ proc_net_remove(&init_net, "mcfilter6");
++ proc_net_remove(&init_net, "igmp6");
+ #endif
+ }
+diff -Nurb linux-2.6.22-570/net/ipv6/mip6.c linux-2.6.22-590/net/ipv6/mip6.c
+--- linux-2.6.22-570/net/ipv6/mip6.c 2007-07-08 19:32:17.000000000 -0400
++++ linux-2.6.22-590/net/ipv6/mip6.c 2008-01-29 22:12:32.000000000 -0500
+@@ -30,6 +30,7 @@
+ #include <net/sock.h>
+ #include <net/ipv6.h>
+ #include <net/ip6_checksum.h>
++#include <net/rawv6.h>
+ #include <net/xfrm.h>
+ #include <net/mip6.h>
+
+@@ -86,7 +87,7 @@
+ return len;
+ }
+
+-int mip6_mh_filter(struct sock *sk, struct sk_buff *skb)
++static int mip6_mh_filter(struct sock *sk, struct sk_buff *skb)
+ {
+ struct ip6_mh *mh;
+
+@@ -471,7 +472,7 @@
+ .remote_addr = mip6_xfrm_addr,
+ };
+
+-int __init mip6_init(void)
++static int __init mip6_init(void)
+ {
+ printk(KERN_INFO "Mobile IPv6\n");
+
+@@ -483,18 +484,35 @@
+ printk(KERN_INFO "%s: can't add xfrm type(rthdr)\n", __FUNCTION__);
+ goto mip6_rthdr_xfrm_fail;
+ }
++ if (rawv6_mh_filter_register(mip6_mh_filter) < 0) {
++ printk(KERN_INFO "%s: can't add rawv6 mh filter\n", __FUNCTION__);
++ goto mip6_rawv6_mh_fail;
++ }
++
++
+ return 0;
+
++ mip6_rawv6_mh_fail:
++ xfrm_unregister_type(&mip6_rthdr_type, AF_INET6);
+ mip6_rthdr_xfrm_fail:
+ xfrm_unregister_type(&mip6_destopt_type, AF_INET6);
+ mip6_destopt_xfrm_fail:
+ return -EAGAIN;
+ }
+
+-void __exit mip6_fini(void)
++static void __exit mip6_fini(void)
+ {
++ if (rawv6_mh_filter_unregister(mip6_mh_filter) < 0)
++ printk(KERN_INFO "%s: can't remove rawv6 mh filter\n", __FUNCTION__);
+ if (xfrm_unregister_type(&mip6_rthdr_type, AF_INET6) < 0)
+ printk(KERN_INFO "%s: can't remove xfrm type(rthdr)\n", __FUNCTION__);
+ if (xfrm_unregister_type(&mip6_destopt_type, AF_INET6) < 0)
+ printk(KERN_INFO "%s: can't remove xfrm type(destopt)\n", __FUNCTION__);
+ }
++
++module_init(mip6_init);
++module_exit(mip6_fini);
++
++MODULE_LICENSE("GPL");