summaryrefslogtreecommitdiffstats
path: root/net/ipv6
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv6')
-rw-r--r--net/ipv6/addrconf.c25
-rw-r--r--net/ipv6/af_inet6.c97
-rw-r--r--net/ipv6/icmp.c43
-rw-r--r--net/ipv6/ip6_fib.c9
-rw-r--r--net/ipv6/ip6_input.c11
-rw-r--r--net/ipv6/ip6_output.c102
-rw-r--r--net/ipv6/ipv6_sockglue.c83
-rw-r--r--net/ipv6/mcast.c39
-rw-r--r--net/ipv6/ndisc.c37
-rw-r--r--net/ipv6/protocol.c4
-rw-r--r--net/ipv6/raw.c4
-rw-r--r--net/ipv6/route.c90
-rw-r--r--net/ipv6/tcp_ipv6.c25
-rw-r--r--net/ipv6/udp.c37
14 files changed, 453 insertions, 153 deletions
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 5571c04c7..329807093 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -5,7 +5,7 @@
* Authors:
* Pedro Roque <roque@di.fc.ul.pt>
*
- * $Id: addrconf.c,v 1.38 1998/03/20 09:12:14 davem Exp $
+ * $Id: addrconf.c,v 1.43 1998/07/15 05:05:32 davem Exp $
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -53,6 +53,7 @@
#include <linux/rtnetlink.h>
#include <asm/uaccess.h>
+#include <asm/delay.h>
/* Set to 3 to get tracing... */
#define ACONF_DEBUG 2
@@ -1033,7 +1034,7 @@ static void addrconf_dev_config(struct device *dev)
struct inet6_dev * idev;
if (dev->type != ARPHRD_ETHER) {
- /* Alas, we support only ethernet autoconfiguration. */
+ /* Alas, we support only Ethernet autoconfiguration. */
return;
}
@@ -1157,13 +1158,6 @@ static int addrconf_ifdown(struct device *dev, int how)
start_bh_atomic();
- /* Discard multicast list */
-
- if (how == 1)
- ipv6_mc_destroy_dev(idev);
- else
- ipv6_mc_down(idev);
-
/* Discard address list */
idev->addr_list = NULL;
@@ -1187,6 +1181,13 @@ static int addrconf_ifdown(struct device *dev, int how)
}
}
+ /* Discard multicast list */
+
+ if (how == 1)
+ ipv6_mc_destroy_dev(idev);
+ else
+ ipv6_mc_down(idev);
+
/* Delete device from device hash table (if unregistered) */
if (how == 1) {
@@ -1608,7 +1609,7 @@ static struct rtnetlink_link inet6_rtnetlink_table[RTM_MAX-RTM_BASE+1] =
{ inet6_rtm_newroute, NULL, },
{ inet6_rtm_delroute, NULL, },
- { NULL, inet6_dump_fib, },
+ { inet6_rtm_getroute, inet6_dump_fib, },
{ NULL, NULL, },
};
#endif
@@ -1808,7 +1809,7 @@ __initfunc(void addrconf_init(void))
addr_chk_timer.expires = jiffies + ADDR_CHECK_FREQUENCY;
add_timer(&addr_chk_timer);
#ifdef CONFIG_RTNETLINK
- rtnetlink_links[AF_INET6] = inet6_rtnetlink_table;
+ rtnetlink_links[PF_INET6] = inet6_rtnetlink_table;
#endif
#ifdef CONFIG_SYSCTL
addrconf_sysctl.sysctl_header =
@@ -1825,7 +1826,7 @@ void addrconf_cleanup(void)
int i;
#ifdef CONFIG_RTNETLINK
- rtnetlink_links[AF_INET6] = NULL;
+ rtnetlink_links[PF_INET6] = NULL;
#endif
#ifdef CONFIG_SYSCTL
addrconf_sysctl_unregister(&ipv6_devconf_dflt);
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
index c1b2e9d14..051f9a28e 100644
--- a/net/ipv6/af_inet6.c
+++ b/net/ipv6/af_inet6.c
@@ -1,5 +1,5 @@
/*
- * AF_INET6 socket family
+ * PF_INET6 socket protocol family
* Linux INET6 implementation
*
* Authors:
@@ -7,7 +7,7 @@
*
* Adapted from linux/net/ipv4/af_inet.c
*
- * $Id: af_inet6.c,v 1.30 1998/03/25 00:23:05 davem Exp $
+ * $Id: af_inet6.c,v 1.36 1998/06/10 07:29:25 davem Exp $
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -66,12 +66,17 @@ extern int udp6_get_info(char *, char **, off_t, int, int);
extern int afinet6_get_info(char *, char **, off_t, int, int);
#endif
+#ifdef CONFIG_SYSCTL
+extern void ipv6_sysctl_register(void);
+extern void ipv6_sysctl_unregister(void);
+#endif
+
static int inet6_create(struct socket *sock, int protocol)
{
struct sock *sk;
struct proto *prot;
- sk = sk_alloc(AF_INET6, GFP_KERNEL, 1);
+ sk = sk_alloc(PF_INET6, GFP_KERNEL, 1);
if (sk == NULL)
goto do_oom;
@@ -105,7 +110,7 @@ static int inet6_create(struct socket *sock, int protocol)
sk->destruct = NULL;
sk->zapped = 0;
- sk->family = AF_INET6;
+ sk->family = PF_INET6;
sk->protocol = protocol;
sk->prot = prot;
@@ -336,7 +341,7 @@ static int inet6_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
}
struct proto_ops inet6_stream_ops = {
- AF_INET6,
+ PF_INET6,
sock_no_dup,
inet6_release,
@@ -357,7 +362,7 @@ struct proto_ops inet6_stream_ops = {
};
struct proto_ops inet6_dgram_ops = {
- AF_INET6,
+ PF_INET6,
sock_no_dup,
inet6_release,
@@ -378,7 +383,7 @@ struct proto_ops inet6_dgram_ops = {
};
struct net_proto_family inet6_family_ops = {
- AF_INET6,
+ PF_INET6,
inet6_create
};
@@ -412,10 +417,16 @@ static struct proc_dir_entry proc_net_sockstat6 = {
#ifdef MODULE
int ipv6_unload(void)
{
- return 0;
+ /* We keep internally 3 raw sockets */
+ return __this_module.usecount - 3;
}
#endif
+#if defined(MODULE) && defined(CONFIG_SYSCTL)
+extern void ipv6_sysctl_register(void);
+extern void ipv6_sysctl_unregister(void);
+#endif
+
#ifdef MODULE
int init_module(void)
#else
@@ -423,6 +434,7 @@ __initfunc(void inet6_proto_init(struct net_proto *pro))
#endif
{
struct sk_buff *dummy_skb;
+ int err;
#ifdef MODULE
if (!mod_member_present(&__this_module, can_unload))
@@ -443,8 +455,6 @@ __initfunc(void inet6_proto_init(struct net_proto *pro))
#endif
}
- (void) sock_register(&inet6_family_ops);
-
/*
* ipngwg API draft makes clear that the correct semantics
* for TCP and UDP is to consider one TCP and UDP instance
@@ -452,19 +462,26 @@ __initfunc(void inet6_proto_init(struct net_proto *pro))
* able to communicate via both network protocols.
*/
- ipv6_init();
-
- icmpv6_init(&inet6_family_ops);
-
- addrconf_init();
-
- sit_init();
-
- /* init v6 transport protocols */
-
+#if defined(MODULE) && defined(CONFIG_SYSCTL)
+ ipv6_sysctl_register();
+#endif
+ err = icmpv6_init(&inet6_family_ops);
+ if (err)
+ goto icmp_fail;
+ err = ndisc_init(&inet6_family_ops);
+ if (err)
+ goto ndisc_fail;
+ err = igmp6_init(&inet6_family_ops);
+ if (err)
+ goto igmp_fail;
+ ipv6_netdev_notif_init();
+ ipv6_packet_init();
+ ip6_route_init();
+ addrconf_init();
+ sit_init();
+
+ /* Init v6 transport protocols. */
udpv6_init();
- /* add /proc entries here */
-
tcpv6_init();
/* Create /proc/foo6 entries. */
@@ -475,22 +492,52 @@ __initfunc(void inet6_proto_init(struct net_proto *pro))
proc_net_register(&proc_net_sockstat6);
#endif
+ /* Now the userspace is allowed to create INET6 sockets. */
+ (void) sock_register(&inet6_family_ops);
+
#ifdef MODULE
return 0;
+#else
+ return;
+#endif
+
+igmp_fail:
+ ndisc_cleanup();
+ndisc_fail:
+ icmpv6_cleanup();
+icmp_fail:
+#if defined(MODULE) && defined(CONFIG_SYSCTL)
+ ipv6_sysctl_unregister();
+#endif
+#ifdef MODULE
+ return err;
+#else
+ return;
#endif
}
#ifdef MODULE
void cleanup_module(void)
{
- sit_cleanup();
- ipv6_cleanup();
- sock_unregister(AF_INET6);
+ /* First of all disallow new sockets creation. */
+ sock_unregister(PF_INET6);
#ifdef CONFIG_PROC_FS
proc_net_unregister(proc_net_raw6.low_ino);
proc_net_unregister(proc_net_tcp6.low_ino);
proc_net_unregister(proc_net_udp6.low_ino);
proc_net_unregister(proc_net_sockstat6.low_ino);
#endif
+ /* Cleanup code parts. */
+ sit_cleanup();
+ ipv6_netdev_notif_cleanup();
+ addrconf_cleanup();
+ ip6_route_cleanup();
+ ipv6_packet_cleanup();
+ igmp6_cleanup();
+ ndisc_cleanup();
+ icmpv6_cleanup();
+#ifdef CONFIG_SYSCTL
+ ipv6_sysctl_unregister();
+#endif
}
#endif /* MODULE */
diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c
index 104895936..c3b6f7b6b 100644
--- a/net/ipv6/icmp.c
+++ b/net/ipv6/icmp.c
@@ -5,7 +5,7 @@
* Authors:
* Pedro Roque <roque@di.fc.ul.pt>
*
- * $Id: icmp.c,v 1.17 1998/05/01 10:31:41 davem Exp $
+ * $Id: icmp.c,v 1.18 1998/05/07 15:42:59 davem Exp $
*
* Based on net/ipv4/icmp.c
*
@@ -62,8 +62,7 @@
* ICMP socket for flow control.
*/
-struct inode icmpv6_inode;
-struct socket *icmpv6_socket=&icmpv6_inode.u.socket_i;
+struct socket *icmpv6_socket;
int icmpv6_rcv(struct sk_buff *skb, struct device *dev,
struct in6_addr *saddr, struct in6_addr *daddr,
@@ -557,19 +556,23 @@ __initfunc(int icmpv6_init(struct net_proto_family *ops))
struct sock *sk;
int err;
- icmpv6_inode.i_mode = S_IFSOCK;
- icmpv6_inode.i_sock = 1;
- icmpv6_inode.i_uid = 0;
- icmpv6_inode.i_gid = 0;
-
- icmpv6_socket->inode = &icmpv6_inode;
- icmpv6_socket->state = SS_UNCONNECTED;
- icmpv6_socket->type=SOCK_RAW;
-
- if((err=ops->create(icmpv6_socket, IPPROTO_ICMPV6))<0) {
- printk(KERN_DEBUG
+ icmpv6_socket = sock_alloc();
+ if (icmpv6_socket == NULL) {
+ printk(KERN_ERR
"Failed to create the ICMP6 control socket.\n");
- return 1;
+ return -1;
+ }
+ icmpv6_socket->inode->i_uid = 0;
+ icmpv6_socket->inode->i_gid = 0;
+ icmpv6_socket->type = SOCK_RAW;
+
+ if ((err = ops->create(icmpv6_socket, IPPROTO_ICMPV6)) < 0) {
+ printk(KERN_ERR
+ "Failed to initialize the ICMP6 control socket (err %d).\n",
+ err);
+ sock_release(icmpv6_socket);
+ icmpv6_socket = NULL; /* for safety */
+ return err;
}
sk = icmpv6_socket->sk;
@@ -578,18 +581,14 @@ __initfunc(int icmpv6_init(struct net_proto_family *ops))
inet6_add_protocol(&icmpv6_protocol);
- ndisc_init(ops);
- igmp6_init(ops);
- return 0;
+ return 0;
}
void icmpv6_cleanup(void)
{
+ sock_release(icmpv6_socket);
+ icmpv6_socket = NULL; /* For safety. */
inet6_del_protocol(&icmpv6_protocol);
-#if 0
- ndisc_cleanup();
-#endif
- igmp6_cleanup();
}
static struct icmp6_err {
diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c
index 693caaf3b..e7e12e3ae 100644
--- a/net/ipv6/ip6_fib.c
+++ b/net/ipv6/ip6_fib.c
@@ -5,7 +5,7 @@
* Authors:
* Pedro Roque <roque@di.fc.ul.pt>
*
- * $Id: ip6_fib.c,v 1.13 1998/04/28 06:22:03 davem Exp $
+ * $Id: ip6_fib.c,v 1.14 1998/05/07 15:43:03 davem Exp $
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -1077,3 +1077,10 @@ void fib6_run_gc(unsigned long dummy)
ip6_fib_timer.expires = 0;
}
}
+
+#ifdef MODULE
+void fib6_gc_cleanup(void)
+{
+ del_timer(&ip6_fib_timer);
+}
+#endif
diff --git a/net/ipv6/ip6_input.c b/net/ipv6/ip6_input.c
index 5f024dddb..6ab4d2c08 100644
--- a/net/ipv6/ip6_input.c
+++ b/net/ipv6/ip6_input.c
@@ -6,7 +6,7 @@
* Pedro Roque <roque@di.fc.ul.pt>
* Ian P. Morris <I.P.Morris@soton.ac.uk>
*
- * $Id: ip6_input.c,v 1.9 1998/04/30 16:24:24 freitag Exp $
+ * $Id: ip6_input.c,v 1.10 1998/07/15 05:05:34 davem Exp $
*
* Based in linux/net/ipv4/ip_input.c
*
@@ -65,11 +65,6 @@ struct hdrtype_proc {
/* New header structures */
-struct ipv6_tlvtype {
- u8 type;
- u8 len;
-};
-
struct tlvtype_proc {
u8 type;
int (*func) (struct sk_buff *, struct device *dev, __u8 *ptr,
@@ -82,7 +77,7 @@ struct tlvtype_proc {
{255, NULL}
};
-static int ip6_dstopt_unknown(struct sk_buff *skb, struct ipv6_tlvtype *hdr)
+int ip6_dstopt_unknown(struct sk_buff *skb, struct ipv6_tlvtype *hdr)
{
struct in6_addr *daddr;
int pos;
@@ -91,7 +86,7 @@ static int ip6_dstopt_unknown(struct sk_buff *skb, struct ipv6_tlvtype *hdr)
* unkown destination option type
*/
- pos = (__u8 *) skb->h.raw - (__u8 *) skb->nh.raw;
+ pos = (__u8 *) hdr - (__u8 *) skb->nh.raw;
/* I think this is correct please check - IPM */
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index eb3984f55..aa13c2074 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -5,7 +5,7 @@
* Authors:
* Pedro Roque <roque@di.fc.ul.pt>
*
- * $Id: ip6_output.c,v 1.12 1998/04/11 22:11:06 davem Exp $
+ * $Id: ip6_output.c,v 1.13 1998/07/15 05:05:38 davem Exp $
*
* Based on linux/net/ipv4/ip_output.c
*
@@ -32,6 +32,7 @@
#include <net/protocol.h>
#include <net/ip6_route.h>
#include <net/addrconf.h>
+#include <net/rawv6.h>
static u32 ipv6_fragmentation_id = 1;
@@ -519,25 +520,104 @@ int ip6_build_xmit(struct sock *sk, inet_getfrag_t getfrag, const void *data,
return err;
}
+int ip6_call_ra_chain(struct sk_buff *skb, int sel)
+{
+ struct ip6_ra_chain *ra;
+ struct sock *last = NULL;
+
+ for (ra = ip6_ra_chain; ra; ra = ra->next) {
+ struct sock *sk = ra->sk;
+ if (sk && ra->sel == sel) {
+ if (last) {
+ struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
+ if (skb2) {
+ skb2->sk = last;
+ rawv6_rcv(skb2, skb2->dev, &skb2->nh.ipv6h->saddr,
+ &skb2->nh.ipv6h->daddr, NULL, skb2->len);
+ }
+ }
+ last = sk;
+ }
+ }
+
+ if (last) {
+ skb->sk = last;
+ rawv6_rcv(skb, skb->dev, &skb->nh.ipv6h->saddr,
+ &skb->nh.ipv6h->daddr, NULL, skb->len);
+ return 1;
+ }
+ return 0;
+}
+
int ip6_forward(struct sk_buff *skb)
{
struct dst_entry *dst = skb->dst;
struct ipv6hdr *hdr = skb->nh.ipv6h;
int size;
- if (ipv6_devconf.forwarding == 0) {
- kfree_skb(skb);
- return -EINVAL;
- }
+ if (ipv6_devconf.forwarding == 0)
+ goto drop;
/*
* check hop-by-hop options present
*/
-#if 0
- if (hdr->nexthdr == NEXTHDR_HOP)
- {
+ /*
+ * Note, that NEXTHDR_HOP header must be checked
+ * always at the most beginning of ipv6_rcv.
+ * The result should be saved somewhere, but
+ * we do not it for now. Alas. Let's do it here. --ANK
+ *
+ * Second note: we DO NOT make any processing on
+ * RA packets, pushing them to user level AS IS
+ * without ane WARRANTY that application will able
+ * to interpret them. The reson is that we
+ * cannot make anything clever here.
+ *
+ * We are not end-node, so that if packet contains
+ * AH/ESP, we cannot make anything.
+ * Defragmentation also would be mistake, RA packets
+ * cannot be fragmented, because there is no warranty
+ * that different fragments will go along one path. --ANK
+ */
+ if (hdr->nexthdr == NEXTHDR_HOP) {
+ int ra_value = -1;
+ u8 *ptr = (u8*)(skb->nh.ipv6h+1);
+ int len = (ptr[1]+1)<<3;
+
+ if (len + sizeof(struct ipv6hdr) > skb->len)
+ goto drop;
+
+ ptr += 2;
+ len -= 2;
+ while (len > 0) {
+ u8 *opt;
+ int optlen;
+
+ if (ptr[0] == 0) {
+ len--;
+ ptr++;
+ continue;
+ }
+ opt = ptr;
+ optlen = ptr[1]+1;
+
+ len -= optlen;
+ ptr += optlen;
+ if (len < 0)
+ goto drop;
+
+ if (opt[0] == 20) {
+ /* Router Alert as of draft-ietf-ipngwg-ipv6router-alert-04 */
+ if (optlen < 4)
+ goto drop;
+ ra_value = opt[2] + (opt[3]<<8);
+ } else if (!ip6_dstopt_unknown(skb, (struct ipv6_tlvtype*)opt))
+ goto drop;
+ }
+ if (ra_value>=0 && ip6_call_ra_chain(skb, ra_value))
+ return 0;
}
-#endif
+
/*
* check and decrement ttl
*/
@@ -589,4 +669,8 @@ int ip6_forward(struct sk_buff *skb)
dst->output(skb);
return 0;
+
+drop:
+ kfree_skb(skb);
+ return -EINVAL;
}
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
index ebd3365cd..b31c07c00 100644
--- a/net/ipv6/ipv6_sockglue.c
+++ b/net/ipv6/ipv6_sockglue.c
@@ -7,7 +7,7 @@
*
* Based on linux/net/ipv4/ip_sockglue.c
*
- * $Id: ipv6_sockglue.c,v 1.19 1998/04/30 16:24:26 freitag Exp $
+ * $Id: ipv6_sockglue.c,v 1.22 1998/07/15 05:05:39 davem Exp $
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -67,6 +67,45 @@ static struct notifier_block ipv6_dev_notf = {
0
};
+struct ip6_ra_chain *ip6_ra_chain;
+
+int ip6_ra_control(struct sock *sk, int sel, void (*destructor)(struct sock *))
+{
+ struct ip6_ra_chain *ra, *new_ra, **rap;
+
+ /* RA packet may be delivered ONLY to IPPROTO_RAW socket */
+ if (sk->type != SOCK_RAW || sk->num != IPPROTO_RAW)
+ return -EINVAL;
+
+ new_ra = (sel>=0) ? kmalloc(sizeof(*new_ra), GFP_KERNEL) : NULL;
+
+ for (rap = &ip6_ra_chain; (ra=*rap) != NULL; rap = &ra->next) {
+ if (ra->sk == sk) {
+ if (sel>=0) {
+ if (new_ra)
+ kfree(new_ra);
+ return -EADDRINUSE;
+ }
+ *rap = ra->next;
+ if (ra->destructor)
+ ra->destructor(sk);
+ kfree(ra);
+ return 0;
+ }
+ }
+ if (new_ra == NULL)
+ return -ENOBUFS;
+ new_ra->sk = sk;
+ new_ra->sel = sel;
+ new_ra->destructor = destructor;
+ start_bh_atomic();
+ new_ra->next = ra;
+ *rap = new_ra;
+ end_bh_atomic();
+ return 0;
+}
+
+
int ipv6_setsockopt(struct sock *sk, int level, int optname, char *optval,
int optlen)
{
@@ -74,6 +113,9 @@ int ipv6_setsockopt(struct sock *sk, int level, int optname, char *optval,
int val, err;
int retv = -ENOPROTOOPT;
+ if(level==SOL_IP && sk->type != SOCK_RAW)
+ return udp_prot.setsockopt(sk, level, optname, optval, optlen);
+
if(level!=SOL_IPV6)
goto out;
@@ -110,7 +152,7 @@ int ipv6_setsockopt(struct sock *sk, int level, int optname, char *optval,
sk->prot = &tcp_prot;
tp->af_specific = &ipv4_specific;
sk->socket->ops = &inet_stream_ops;
- sk->family = AF_INET;
+ sk->family = PF_INET;
} else {
sk->prot = &udp_prot;
sk->socket->ops = &inet_dgram_ops;
@@ -197,7 +239,11 @@ int ipv6_setsockopt(struct sock *sk, int level, int optname, char *optval,
retv = ipv6_sock_mc_join(sk, mreq.ipv6mr_ifindex, &mreq.ipv6mr_multiaddr);
else
retv = ipv6_sock_mc_drop(sk, mreq.ipv6mr_ifindex, &mreq.ipv6mr_multiaddr);
+ break;
}
+ case IPV6_ROUTER_ALERT:
+ retv = ip6_ra_control(sk, val, NULL);
+ break;
};
out:
@@ -207,7 +253,11 @@ out:
int ipv6_getsockopt(struct sock *sk, int level, int optname, char *optval,
int *optlen)
{
- return 0;
+ if(level==SOL_IP && sk->type != SOCK_RAW)
+ return udp_prot.getsockopt(sk, level, optname, optval, optlen);
+ if(level!=SOL_IPV6)
+ return -ENOPROTOOPT;
+ return -EINVAL;
}
#if defined(MODULE) && defined(CONFIG_SYSCTL)
@@ -220,31 +270,24 @@ extern void ipv6_sysctl_register(void);
extern void ipv6_sysctl_unregister(void);
#endif
-__initfunc(void ipv6_init(void))
+__initfunc(void ipv6_packet_init(void))
{
dev_add_pack(&ipv6_packet_type);
+}
-#if defined(MODULE) && defined(CONFIG_SYSCTL)
- ipv6_sysctl_register();
-#endif
-
+__initfunc(void ipv6_netdev_notif_init(void))
+{
register_netdevice_notifier(&ipv6_dev_notf);
-
- ip6_route_init();
}
#ifdef MODULE
-void ipv6_cleanup(void)
+void ipv6_packet_cleanup(void)
{
- unregister_netdevice_notifier(&ipv6_dev_notf);
dev_remove_pack(&ipv6_packet_type);
-#ifdef CONFIG_SYSCTL
- ipv6_sysctl_unregister();
-#endif
- ip6_route_cleanup();
- icmpv6_cleanup();
- addrconf_cleanup();
}
-#endif
-
+void ipv6_netdev_notif_cleanup(void)
+{
+ unregister_netdevice_notifier(&ipv6_dev_notf);
+}
+#endif
diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
index 0e10dcf0b..c50f37fcf 100644
--- a/net/ipv6/mcast.c
+++ b/net/ipv6/mcast.c
@@ -5,7 +5,7 @@
* Authors:
* Pedro Roque <roque@di.fc.ul.pt>
*
- * $Id: mcast.c,v 1.15 1998/04/30 16:24:28 freitag Exp $
+ * $Id: mcast.c,v 1.16 1998/05/07 15:43:10 davem Exp $
*
* Based on linux/ipv4/igmp.c and linux/ipv4/ip_sockglue.c
*
@@ -52,8 +52,7 @@
#define MDBG(x)
#endif
-static struct inode igmp6_inode;
-static struct socket *igmp6_socket=&igmp6_inode.u.socket_i;
+static struct socket *igmp6_socket;
static void igmp6_join_group(struct ifmcaddr6 *ma);
static void igmp6_leave_group(struct ifmcaddr6 *ma);
@@ -598,7 +597,7 @@ done:
}
#endif
-__initfunc(void igmp6_init(struct net_proto_family *ops))
+__initfunc(int igmp6_init(struct net_proto_family *ops))
{
#ifdef CONFIG_PROC_FS
struct proc_dir_entry *ent;
@@ -606,18 +605,24 @@ __initfunc(void igmp6_init(struct net_proto_family *ops))
struct sock *sk;
int err;
- igmp6_inode.i_mode = S_IFSOCK;
- igmp6_inode.i_sock = 1;
- igmp6_inode.i_uid = 0;
- igmp6_inode.i_gid = 0;
-
- igmp6_socket->inode = &igmp6_inode;
- igmp6_socket->state = SS_UNCONNECTED;
+ igmp6_socket = sock_alloc();
+ if (igmp6_socket == NULL) {
+ printk(KERN_ERR
+ "Failed to create the IGMP6 control socket.\n");
+ return -1;
+ }
+ igmp6_socket->inode->i_uid = 0;
+ igmp6_socket->inode->i_gid = 0;
igmp6_socket->type = SOCK_RAW;
- if((err=ops->create(igmp6_socket, IPPROTO_ICMPV6))<0)
+ if((err = ops->create(igmp6_socket, IPPROTO_ICMPV6)) < 0) {
printk(KERN_DEBUG
- "Failed to create the IGMP6 control socket.\n");
+ "Failed to initialize the IGMP6 control socket (err %d).\n",
+ err);
+ sock_release(igmp6_socket);
+ igmp6_socket = NULL; /* For safety. */
+ return err;
+ }
sk = igmp6_socket->sk;
sk->allocation = GFP_ATOMIC;
@@ -628,11 +633,17 @@ __initfunc(void igmp6_init(struct net_proto_family *ops))
ent = create_proc_entry("net/igmp6", 0, 0);
ent->read_proc = igmp6_read_proc;
#endif
+
+ return 0;
}
+#ifdef MODULE
void igmp6_cleanup(void)
{
+ sock_release(igmp6_socket);
+ igmp6_socket = NULL; /* for safety */
#ifdef CONFIG_PROC_FS
- remove_proc_entry("net/igmp6", 0);
+ remove_proc_entry("net/igmp6", 0);
#endif
}
+#endif
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index e69d90332..26e42a1ed 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -74,8 +74,7 @@
#include <net/checksum.h>
#include <linux/proc_fs.h>
-static struct inode ndisc_inode;
-static struct socket *ndisc_socket=&ndisc_inode.u.socket_i;
+static struct socket *ndisc_socket;
static int ndisc_constructor(struct neighbour *neigh);
static void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb);
@@ -1134,23 +1133,29 @@ struct proc_dir_entry ndisc_proc_entry =
-__initfunc(void ndisc_init(struct net_proto_family *ops))
+__initfunc(int ndisc_init(struct net_proto_family *ops))
{
struct sock *sk;
int err;
- ndisc_inode.i_mode = S_IFSOCK;
- ndisc_inode.i_sock = 1;
- ndisc_inode.i_uid = 0;
- ndisc_inode.i_gid = 0;
-
- ndisc_socket->inode = &ndisc_inode;
- ndisc_socket->state = SS_UNCONNECTED;
- ndisc_socket->type = SOCK_RAW;
+ ndisc_socket = sock_alloc();
+ if (ndisc_socket == NULL) {
+ printk(KERN_ERR
+ "Failed to create the NDISC control socket.\n");
+ return -1;
+ }
+ ndisc_socket->inode->i_uid = 0;
+ ndisc_socket->inode->i_gid = 0;
+ ndisc_socket->type = SOCK_RAW;
- if((err=ops->create(ndisc_socket, IPPROTO_ICMPV6))<0)
+ if((err = ops->create(ndisc_socket, IPPROTO_ICMPV6)) < 0) {
printk(KERN_DEBUG
- "Failed to create the NDISC control socket.\n");
+ "Failed to initializee the NDISC control socket (err %d).\n",
+ err);
+ sock_release(ndisc_socket);
+ ndisc_socket = NULL; /* For safety. */
+ return err;
+ }
sk = ndisc_socket->sk;
sk->allocation = GFP_ATOMIC;
@@ -1174,9 +1179,10 @@ __initfunc(void ndisc_init(struct net_proto_family *ops))
#ifdef CONFIG_SYSCTL
neigh_sysctl_register(NULL, &nd_tbl.parms, NET_IPV6, NET_IPV6_NEIGH, "ipv6");
#endif
+
+ return 0;
}
-#ifdef MODULE
void ndisc_cleanup(void)
{
#ifdef CONFIG_PROC_FS
@@ -1185,5 +1191,6 @@ void ndisc_cleanup(void)
#endif
#endif
neigh_table_clear(&nd_tbl);
+ sock_release(ndisc_socket);
+ ndisc_socket = NULL; /* For safety. */
}
-#endif
diff --git a/net/ipv6/protocol.c b/net/ipv6/protocol.c
index 3ec242adb..8a5ae0654 100644
--- a/net/ipv6/protocol.c
+++ b/net/ipv6/protocol.c
@@ -3,9 +3,9 @@
* operating system. INET is implemented using the BSD Socket
* interface as the means of communication with the user level.
*
- * AF_INET6 protocol dispatch tables.
+ * PF_INET6 protocol dispatch tables.
*
- * Version: $Id: protocol.c,v 1.5 1997/03/18 18:24:44 davem Exp $
+ * Version: $Id: protocol.c,v 1.6 1998/05/03 14:31:09 alan Exp $
*
* Authors: Pedro Roque <roque@di.fc.ul.pt>
*
diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c
index 7429a9210..659ec59cc 100644
--- a/net/ipv6/raw.c
+++ b/net/ipv6/raw.c
@@ -7,7 +7,7 @@
*
* Adapted from linux/net/ipv4/raw.c
*
- * $Id: raw.c,v 1.19 1998/03/20 09:12:20 davem Exp $
+ * $Id: raw.c,v 1.20 1998/07/15 05:05:41 davem Exp $
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -596,6 +596,8 @@ static void rawv6_close(struct sock *sk, unsigned long timeout)
{
sk->state = TCP_CLOSE;
ipv6_sock_mc_close(sk);
+ if (sk->num == IPPROTO_RAW)
+ ip6_ra_control(sk, -1, NULL);
sk->dead = 1;
destroy_sock(sk);
}
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index 3baa41007..9d159fe36 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -5,7 +5,7 @@
* Authors:
* Pedro Roque <roque@di.fc.ul.pt>
*
- * $Id: route.c,v 1.28 1998/04/28 06:22:04 davem Exp $
+ * $Id: route.c,v 1.32 1998/07/25 23:28:52 davem Exp $
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -1722,7 +1722,6 @@ int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
return err;
}
-
struct rt6_rtnl_dump_arg
{
struct sk_buff *skb;
@@ -1733,6 +1732,9 @@ struct rt6_rtnl_dump_arg
};
static int rt6_fill_node(struct sk_buff *skb, struct rt6_info *rt,
+ struct in6_addr *dst,
+ struct in6_addr *src,
+ int iif,
int type, pid_t pid, u32 seq)
{
struct rtmsg *rtm;
@@ -1777,10 +1779,23 @@ static int rt6_fill_node(struct sk_buff *skb, struct rt6_info *rt,
#ifdef CONFIG_RTNL_OLD_IFINFO
o = skb->tail;
#endif
- if (rtm->rtm_dst_len)
+ if (dst) {
+ RTA_PUT(skb, RTA_DST, 16, dst);
+ rtm->rtm_dst_len = 128;
+ } else if (rtm->rtm_dst_len)
RTA_PUT(skb, RTA_DST, 16, &rt->rt6i_dst.addr);
- if (rtm->rtm_src_len)
+ if (src) {
+ RTA_PUT(skb, RTA_SRC, 16, src);
+ rtm->rtm_src_len = 128;
+ } else if (rtm->rtm_src_len)
RTA_PUT(skb, RTA_SRC, 16, &rt->rt6i_src.addr);
+ if (iif)
+ RTA_PUT(skb, RTA_IIF, 4, &iif);
+ else if (dst) {
+ struct inet6_ifaddr *ifp = ipv6_get_saddr(&rt->u.dst, dst);
+ if (ifp)
+ RTA_PUT(skb, RTA_PREFSRC, 16, &ifp->addr);
+ }
#ifdef CONFIG_RTNL_OLD_IFINFO
if (rt->u.dst.pmtu)
RTA_PUT(skb, RTA_MTU, sizeof(unsigned), &rt->u.dst.pmtu);
@@ -1842,7 +1857,7 @@ static void rt6_dump_node(struct fib6_node *fn, void *p_arg)
arg->count++;
continue;
}
- if (rt6_fill_node(arg->skb, rt, RTM_NEWROUTE,
+ if (rt6_fill_node(arg->skb, rt, NULL, NULL, 0, RTM_NEWROUTE,
NETLINK_CB(arg->cb->skb).pid, arg->cb->nlh->nlmsg_seq) <= 0) {
arg->stop = 1;
break;
@@ -1870,6 +1885,68 @@ int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb)
return skb->len;
}
+int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void *arg)
+{
+ struct rtattr **rta = arg;
+ int iif = 0;
+ int err;
+ struct sk_buff *skb;
+ struct flowi fl;
+ struct rt6_info *rt;
+
+ skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
+ if (skb == NULL)
+ return -ENOBUFS;
+
+ /* Reserve room for dummy headers, this skb can pass
+ through good chunk of routing engine.
+ */
+ skb->mac.raw = skb->data;
+ skb_reserve(skb, MAX_HEADER + sizeof(struct ipv6hdr));
+
+ fl.proto = 0;
+ fl.nl_u.ip6_u.daddr = NULL;
+ fl.nl_u.ip6_u.saddr = NULL;
+ fl.uli_u.icmpt.type = 0;
+ fl.uli_u.icmpt.code = 0;
+ if (rta[RTA_SRC-1])
+ fl.nl_u.ip6_u.saddr = (struct in6_addr*)RTA_DATA(rta[RTA_SRC-1]);
+ if (rta[RTA_DST-1])
+ fl.nl_u.ip6_u.daddr = (struct in6_addr*)RTA_DATA(rta[RTA_DST-1]);
+
+ if (rta[RTA_IIF-1])
+ memcpy(&iif, RTA_DATA(rta[RTA_IIF-1]), sizeof(int));
+
+ if (iif) {
+ struct device *dev;
+ dev = dev_get_by_index(iif);
+ if (!dev)
+ return -ENODEV;
+ }
+
+ fl.oif = 0;
+ if (rta[RTA_OIF-1])
+ memcpy(&fl.oif, RTA_DATA(rta[RTA_OIF-1]), sizeof(int));
+
+ rt = (struct rt6_info*)ip6_route_output(NULL, &fl);
+
+ skb->dst = &rt->u.dst;
+
+ NETLINK_CB(skb).dst_pid = NETLINK_CB(in_skb).pid;
+ err = rt6_fill_node(skb, rt,
+ fl.nl_u.ip6_u.daddr,
+ fl.nl_u.ip6_u.saddr,
+ iif,
+ RTM_NEWROUTE, NETLINK_CB(in_skb).pid, nlh->nlmsg_seq);
+ if (err < 0)
+ return -EMSGSIZE;
+
+ err = netlink_unicast(rtnl, skb, NETLINK_CB(in_skb).pid, MSG_DONTWAIT);
+ if (err < 0)
+ return err;
+ return 0;
+}
+
void inet6_rt_notify(int event, struct rt6_info *rt)
{
struct sk_buff *skb;
@@ -1880,7 +1957,7 @@ void inet6_rt_notify(int event, struct rt6_info *rt)
netlink_set_err(rtnl, 0, RTMGRP_IPV6_ROUTE, ENOBUFS);
return;
}
- if (rt6_fill_node(skb, rt, event, 0, 0) < 0) {
+ if (rt6_fill_node(skb, rt, NULL, NULL, 0, event, 0, 0) < 0) {
kfree_skb(skb);
netlink_set_err(rtnl, 0, RTMGRP_IPV6_ROUTE, EINVAL);
return;
@@ -2173,5 +2250,6 @@ void ip6_route_cleanup(void)
netlink_detach(NETLINK_ROUTE6);
#endif
rt6_ifdown(NULL);
+ fib6_gc_cleanup();
}
#endif /* MODULE */
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index 0a4a95c7c..5fa45dce5 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -5,7 +5,7 @@
* Authors:
* Pedro Roque <roque@di.fc.ul.pt>
*
- * $Id: tcp_ipv6.c,v 1.80 1998/05/02 12:47:15 davem Exp $
+ * $Id: tcp_ipv6.c,v 1.82 1998/06/11 03:15:52 davem Exp $
*
* Based on:
* linux/net/ipv4/tcp.c
@@ -64,9 +64,7 @@ static __inline__ int tcp_v6_hashfn(struct in6_addr *laddr, u16 lport,
{
int hashent = (lport ^ fport);
- hashent ^= (laddr->s6_addr32[0] ^ laddr->s6_addr32[1]);
- hashent ^= (faddr->s6_addr32[0] ^ faddr->s6_addr32[1]);
- hashent ^= (faddr->s6_addr32[2] ^ faddr->s6_addr32[3]);
+ hashent ^= (laddr->s6_addr32[3] ^ faddr->s6_addr32[3]);
return (hashent & ((TCP_HTABLE_SIZE/2) - 1));
}
@@ -145,6 +143,13 @@ go_like_smoke:
static void tcp_v6_hash(struct sock *sk)
{
+ /* Well, I know that it is ugly...
+ All this ->prot, ->af_specific etc. need LARGE cleanup --ANK
+ */
+ if (sk->tp_pinfo.af_tcp.af_specific == &ipv6_mapped) {
+ tcp_prot.hash(sk);
+ return;
+ }
if(sk->state != TCP_CLOSE) {
struct sock **skp;
@@ -213,7 +218,7 @@ static struct sock *tcp_v6_lookup_listener(struct in6_addr *daddr, unsigned shor
hiscore=0;
sk = tcp_listening_hash[tcp_lhashfn(hnum)];
for(; sk; sk = sk->next) {
- if((sk->num == hnum) && (sk->family == AF_INET6)) {
+ if((sk->num == hnum) && (sk->family == PF_INET6)) {
struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
score = 1;
@@ -272,7 +277,7 @@ static inline struct sock *__tcp_v6_lookup(struct tcphdr *th,
/* Must check for a TIME_WAIT'er before going to listener hash. */
for(sk = tcp_established_hash[hash+(TCP_HTABLE_SIZE/2)]; sk; sk = sk->next) {
if(*((__u32 *)&(sk->dport)) == ports &&
- sk->family == AF_INET6) {
+ sk->family == PF_INET6) {
struct tcp_tw_bucket *tw = (struct tcp_tw_bucket *)sk;
if(!ipv6_addr_cmp(&tw->v6_daddr, saddr) &&
!ipv6_addr_cmp(&tw->v6_rcv_saddr, daddr) &&
@@ -415,8 +420,14 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
if (err) {
sk->tp_pinfo.af_tcp.af_specific = &ipv6_specific;
sk->backlog_rcv = tcp_v6_do_rcv;
+ } else {
+ /* Yuup... And it is not the only place... --ANK */
+ ipv6_addr_set(&np->saddr, 0, 0, __constant_htonl(0x0000FFFF),
+ sk->saddr);
+ ipv6_addr_set(&np->rcv_saddr, 0, 0, __constant_htonl(0x0000FFFF),
+ sk->rcv_saddr);
}
-
+
return err;
}
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index 6078ab679..2dac0570f 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -7,7 +7,7 @@
*
* Based on linux/ipv4/udp.c
*
- * $Id: udp.c,v 1.27 1998/03/21 07:28:06 davem Exp $
+ * $Id: udp.c,v 1.31 1998/07/15 05:05:45 davem Exp $
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -147,7 +147,7 @@ static struct sock *udp_v6_lookup(struct in6_addr *saddr, u16 sport,
for(sk = udp_hash[hnum & (UDP_HTABLE_SIZE - 1)]; sk != NULL; sk = sk->next) {
if((sk->num == hnum) &&
- (sk->family == AF_INET6) &&
+ (sk->family == PF_INET6) &&
!(sk->dead && (sk->state == TCP_CLOSE))) {
struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
int score = 0;
@@ -185,12 +185,18 @@ static struct sock *udp_v6_lookup(struct in6_addr *saddr, u16 sport,
int udpv6_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
{
struct sockaddr_in6 *usin = (struct sockaddr_in6 *) uaddr;
+ struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
struct in6_addr *daddr;
struct dst_entry *dst;
- struct ipv6_pinfo *np;
struct inet6_ifaddr *ifa;
struct flowi fl;
int addr_type;
+ int err;
+
+ if (usin->sin6_family == AF_INET) {
+ err = udp_connect(sk, uaddr, addr_len);
+ goto ipv4_connected;
+ }
if (addr_len < sizeof(*usin))
return(-EINVAL);
@@ -199,7 +205,6 @@ int udpv6_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
return(-EAFNOSUPPORT);
addr_type = ipv6_addr_type(&usin->sin6_addr);
- np = &sk->net_pinfo.af_inet6;
if (addr_type == IPV6_ADDR_ANY) {
/*
@@ -212,18 +217,21 @@ int udpv6_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
if (addr_type == IPV6_ADDR_MAPPED) {
struct sockaddr_in sin;
- int err;
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = daddr->s6_addr32[3];
+ sin.sin_port = usin->sin6_port;
err = udp_connect(sk, (struct sockaddr*) &sin, sizeof(sin));
-
+
+ipv4_connected:
if (err < 0)
return err;
- ipv6_addr_copy(&np->daddr, daddr);
-
+ ipv6_addr_set(&np->daddr, 0, 0,
+ __constant_htonl(0x0000ffff),
+ sk->daddr);
+
if(ipv6_addr_any(&np->saddr)) {
ipv6_addr_set(&np->saddr, 0, 0,
__constant_htonl(0x0000ffff),
@@ -236,7 +244,7 @@ int udpv6_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
__constant_htonl(0x0000ffff),
sk->rcv_saddr);
}
-
+ return 0;
}
ipv6_addr_copy(&np->daddr, daddr);
@@ -347,6 +355,8 @@ int udpv6_recvmsg(struct sock *sk, struct msghdr *msg, int len,
if (skb->protocol == __constant_htons(ETH_P_IP)) {
ipv6_addr_set(&sin6->sin6_addr, 0, 0,
__constant_htonl(0xffff), skb->nh.iph->saddr);
+ if (sk->ip_cmsg_flags)
+ ip_cmsg_recv(msg, skb);
} else {
memcpy(&sin6->sin6_addr, &skb->nh.ipv6h->saddr,
sizeof(struct in6_addr));
@@ -668,6 +678,9 @@ static int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, int ulen)
return(-EINVAL);
if (sin6) {
+ if (sin6->sin6_family == AF_INET)
+ return udp_sendmsg(sk, msg, ulen);
+
if (addr_len < sizeof(*sin6))
return(-EINVAL);
@@ -689,7 +702,7 @@ static int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, int ulen)
}
} else {
if (sk->state != TCP_ESTABLISHED)
- return(-EINVAL);
+ return(-ENOTCONN);
udh.uh.dest = sk->dport;
daddr = &sk->net_pinfo.af_inet6.daddr;
@@ -702,8 +715,10 @@ static int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, int ulen)
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = daddr->s6_addr32[3];
+ sin.sin_port = udh.uh.dest;
+ msg->msg_name = (struct sockaddr *)(&sin);
- return udp_sendmsg(sk, msg, len);
+ return udp_sendmsg(sk, msg, ulen);
}
udh.daddr = NULL;