X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=net%2Fsctp%2Fipv6.c;h=99c0cefc04e072ff49b1b794777d7cb904a6cd95;hb=67da514125f23d27bd45d41e330ef14b6206fe69;hp=af402f7d492cccd8e1356f25a78824783c408307;hpb=5273a3df6485dc2ad6aa7ddd441b9a21970f003b;p=linux-2.6.git diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c index af402f7d4..99c0cefc0 100644 --- a/net/sctp/ipv6.c +++ b/net/sctp/ipv6.c @@ -66,8 +66,8 @@ #include #include -#include #include +#include #include #include #include @@ -78,17 +78,19 @@ #include -extern struct notifier_block sctp_inetaddr_notifier; +extern int sctp_inetaddr_event(struct notifier_block *, unsigned long, void *); +static struct notifier_block sctp_inet6addr_notifier = { + .notifier_call = sctp_inetaddr_event, +}; /* ICMP error handler. */ -void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, - int type, int code, int offset, __u32 info) +SCTP_STATIC void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, + int type, int code, int offset, __u32 info) { struct inet6_dev *idev; struct ipv6hdr *iph = (struct ipv6hdr *)skb->data; struct sctphdr *sh = (struct sctphdr *)(skb->data + offset); struct sock *sk; - struct sctp_endpoint *ep; struct sctp_association *asoc; struct sctp_transport *transport; struct ipv6_pinfo *np; @@ -102,12 +104,12 @@ void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, savesctp = skb->h.raw; skb->nh.ipv6h = iph; skb->h.raw = (char *)sh; - sk = sctp_err_lookup(AF_INET6, skb, sh, &ep, &asoc, &transport); + sk = sctp_err_lookup(AF_INET6, skb, sh, &asoc, &transport); /* Put back, the original pointers. */ skb->nh.raw = saveip; skb->h.raw = savesctp; if (!sk) { - ICMP6_INC_STATS_BH(idev, Icmp6InErrors); + ICMP6_INC_STATS_BH(idev, ICMP6_MIB_INERRORS); goto out; } @@ -119,6 +121,12 @@ void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, case ICMPV6_PKT_TOOBIG: sctp_icmp_frag_needed(sk, asoc, transport, ntohl(info)); goto out_unlock; + case ICMPV6_PARAMPROB: + if (ICMPV6_UNK_NEXTHDR == code) { + sctp_icmp_proto_unreachable(sk, asoc, transport); + goto out_unlock; + } + break; default: break; } @@ -133,7 +141,7 @@ void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, } out_unlock: - sctp_err_finish(sk, ep, asoc); + sctp_err_finish(sk, asoc); out: if (likely(idev != NULL)) in6_dev_put(idev); @@ -172,12 +180,11 @@ static int sctp_v6_xmit(struct sk_buff *skb, struct sctp_transport *transport, } SCTP_DEBUG_PRINTK("%s: skb:%p, len:%d, " - "src:%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x " - "dst:%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n", + "src:" NIP6_FMT " dst:" NIP6_FMT "\n", __FUNCTION__, skb, skb->len, NIP6(fl.fl6_src), NIP6(fl.fl6_dst)); - SCTP_INC_STATS(SctpOutSCTPPacks); + SCTP_INC_STATS(SCTP_MIB_OUTSCTPPACKS); return ip6_xmit(sk, skb, &fl, np->opt, ipfragok); } @@ -185,9 +192,9 @@ static int sctp_v6_xmit(struct sk_buff *skb, struct sctp_transport *transport, /* Returns the dst cache entry for the given source and destination ip * addresses. */ -struct dst_entry *sctp_v6_get_dst(struct sctp_association *asoc, - union sctp_addr *daddr, - union sctp_addr *saddr) +static struct dst_entry *sctp_v6_get_dst(struct sctp_association *asoc, + union sctp_addr *daddr, + union sctp_addr *saddr) { struct dst_entry *dst; struct flowi fl; @@ -198,13 +205,13 @@ struct dst_entry *sctp_v6_get_dst(struct sctp_association *asoc, fl.oif = daddr->v6.sin6_scope_id; - SCTP_DEBUG_PRINTK("%s: DST=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ", + SCTP_DEBUG_PRINTK("%s: DST=" NIP6_FMT " ", __FUNCTION__, NIP6(fl.fl6_dst)); if (saddr) { ipv6_addr_copy(&fl.fl6_src, &saddr->v6.sin6_addr); SCTP_DEBUG_PRINTK( - "SRC=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x - ", + "SRC=" NIP6_FMT " - ", NIP6(fl.fl6_src)); } @@ -213,8 +220,7 @@ struct dst_entry *sctp_v6_get_dst(struct sctp_association *asoc, struct rt6_info *rt; rt = (struct rt6_info *)dst; SCTP_DEBUG_PRINTK( - "rt6_dst:%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x " - "rt6_src:%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n", + "rt6_dst:" NIP6_FMT " rt6_src:" NIP6_FMT "\n", NIP6(rt->rt6i_dst.addr), NIP6(rt->rt6i_src.addr)); } else { SCTP_DEBUG_PRINTK("NO ROUTE\n"); @@ -248,8 +254,10 @@ static inline int sctp_v6_addr_match_len(union sctp_addr *s1, /* Fills in the source address(saddr) based on the destination address(daddr) * and asoc's bind address list. */ -void sctp_v6_get_saddr(struct sctp_association *asoc, struct dst_entry *dst, - union sctp_addr *daddr, union sctp_addr *saddr) +static void sctp_v6_get_saddr(struct sctp_association *asoc, + struct dst_entry *dst, + union sctp_addr *daddr, + union sctp_addr *saddr) { struct sctp_bind_addr *bp; rwlock_t *addr_lock; @@ -261,13 +269,12 @@ void sctp_v6_get_saddr(struct sctp_association *asoc, struct dst_entry *dst, __u8 bmatchlen; SCTP_DEBUG_PRINTK("%s: asoc:%p dst:%p " - "daddr:%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ", + "daddr:" NIP6_FMT " ", __FUNCTION__, asoc, dst, NIP6(daddr->v6.sin6_addr)); if (!asoc) { ipv6_get_saddr(dst, &daddr->v6.sin6_addr,&saddr->v6.sin6_addr); - SCTP_DEBUG_PRINTK("saddr from ipv6_get_saddr: " - "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n", + SCTP_DEBUG_PRINTK("saddr from ipv6_get_saddr: " NIP6_FMT "\n", NIP6(saddr->v6.sin6_addr)); return; } @@ -283,7 +290,8 @@ void sctp_v6_get_saddr(struct sctp_association *asoc, struct dst_entry *dst, sctp_read_lock(addr_lock); list_for_each(pos, &bp->address_list) { laddr = list_entry(pos, struct sctp_sockaddr_entry, list); - if ((laddr->a.sa.sa_family == AF_INET6) && + if ((laddr->use_as_src) && + (laddr->a.sa.sa_family == AF_INET6) && (scope <= sctp_scope(&laddr->a))) { bmatchlen = sctp_v6_addr_match_len(daddr, &laddr->a); if (!baddr || (matchlen < bmatchlen)) { @@ -295,13 +303,11 @@ void sctp_v6_get_saddr(struct sctp_association *asoc, struct dst_entry *dst, if (baddr) { memcpy(saddr, baddr, sizeof(union sctp_addr)); - SCTP_DEBUG_PRINTK("saddr: " - "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n", + SCTP_DEBUG_PRINTK("saddr: " NIP6_FMT "\n", NIP6(saddr->v6.sin6_addr)); } else { printk(KERN_ERR "%s: asoc:%p Could not find a valid source " - "address for the " - "dest:%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n", + "address for the dest:" NIP6_FMT "\n", __FUNCTION__, asoc, NIP6(daddr->v6.sin6_addr)); } @@ -461,7 +467,7 @@ static int sctp_v6_cmp_addr(const union sctp_addr *addr1, } return 0; } - if (ipv6_addr_cmp(&addr1->v6.sin6_addr, &addr2->v6.sin6_addr)) + if (!ipv6_addr_equal(&addr1->v6.sin6_addr, &addr2->v6.sin6_addr)) return 0; /* If this is a linklocal address, compare the scope_id. */ if (ipv6_addr_type(&addr1->v6.sin6_addr) & IPV6_ADDR_LINKLOCAL) { @@ -485,13 +491,11 @@ static void sctp_v6_inaddr_any(union sctp_addr *addr, unsigned short port) /* Is this a wildcard address? */ static int sctp_v6_is_any(const union sctp_addr *addr) { - int type; - type = ipv6_addr_type((struct in6_addr *)&addr->v6.sin6_addr); - return IPV6_ADDR_ANY == type; + return ipv6_addr_any(&addr->v6.sin6_addr); } /* Should this be available for binding? */ -static int sctp_v6_available(union sctp_addr *addr, struct sctp_opt *sp) +static int sctp_v6_available(union sctp_addr *addr, struct sctp_sock *sp) { int type; struct in6_addr *in6 = (struct in6_addr *)&addr->v6.sin6_addr; @@ -520,21 +524,23 @@ static int sctp_v6_available(union sctp_addr *addr, struct sctp_opt *sp) * Return 0 - If the address is a non-unicast or an illegal address. * Return 1 - If the address is a unicast. */ -static int sctp_v6_addr_valid(union sctp_addr *addr, struct sctp_opt *sp) +static int sctp_v6_addr_valid(union sctp_addr *addr, + struct sctp_sock *sp, + const struct sk_buff *skb) { int ret = ipv6_addr_type(&addr->v6.sin6_addr); /* Support v4-mapped-v6 address. */ if (ret == IPV6_ADDR_MAPPED) { /* Note: This routine is used in input, so v4-mapped-v6 - * are disallowed here when there is no sctp_opt. + * are disallowed here when there is no sctp_sock. */ if (!sp || !sp->v4mapped) return 0; if (sp && ipv6_only_sock(sctp_opt2sk(sp))) return 0; sctp_v6_map_v4(addr); - return sctp_get_af_specific(AF_INET)->addr_valid(addr, sp); + return sctp_get_af_specific(AF_INET)->addr_valid(addr, sp, skb); } /* Is this a non-unicast address */ @@ -574,22 +580,20 @@ static sctp_scope_t sctp_v6_scope(union sctp_addr *addr) } /* Create and initialize a new sk for the socket to be returned by accept(). */ -struct sock *sctp_v6_create_accept_sk(struct sock *sk, - struct sctp_association *asoc) +static struct sock *sctp_v6_create_accept_sk(struct sock *sk, + struct sctp_association *asoc) { - struct inet_opt *inet = inet_sk(sk); + struct inet_sock *inet = inet_sk(sk); struct sock *newsk; - struct inet_opt *newinet; + struct inet_sock *newinet; struct ipv6_pinfo *newnp, *np = inet6_sk(sk); struct sctp6_sock *newsctp6sk; - newsk = sk_alloc(PF_INET6, GFP_KERNEL, sizeof(struct sctp6_sock), - sk->sk_slab); + newsk = sk_alloc(PF_INET6, GFP_KERNEL, sk->sk_prot, 1); if (!newsk) goto out; sock_init_data(NULL, newsk); - sk_set_owner(newsk, THIS_MODULE); newsk->sk_type = SOCK_STREAM; @@ -598,14 +602,14 @@ struct sock *sctp_v6_create_accept_sk(struct sock *sk, newsk->sk_reuse = sk->sk_reuse; newsk->sk_destruct = inet_sock_destruct; - newsk->sk_zapped = 0; newsk->sk_family = PF_INET6; newsk->sk_protocol = IPPROTO_SCTP; newsk->sk_backlog_rcv = sk->sk_prot->backlog_rcv; newsk->sk_shutdown = sk->sk_shutdown; + sock_reset_flag(sk, SOCK_ZAPPED); newsctp6sk = (struct sctp6_sock *)newsk; - newsctp6sk->pinet6 = &newsctp6sk->inet6; + inet_sk(newsk)->pinet6 = &newsctp6sk->inet6; newinet = inet_sk(newsk); newnp = inet6_sk(newsk); @@ -635,13 +639,10 @@ struct sock *sctp_v6_create_accept_sk(struct sock *sk, else newinet->pmtudisc = IP_PMTUDISC_WANT; -#ifdef INET_REFCNT_DEBUG - atomic_inc(&inet6_sock_nr); - atomic_inc(&inet_sock_nr); -#endif + sk_refcnt_debug_inc(newsk); if (newsk->sk_prot->init(newsk)) { - inet_sock_release(newsk); + sk_common_release(newsk); newsk = NULL; } @@ -650,7 +651,7 @@ out: } /* Map v4 address to mapped v6 address */ -static void sctp_v6_addr_v4map(struct sctp_opt *sp, union sctp_addr *addr) +static void sctp_v6_addr_v4map(struct sctp_sock *sp, union sctp_addr *addr) { if (sp->v4mapped && AF_INET == addr->sa.sa_family) sctp_v4_map_v6(addr); @@ -672,8 +673,7 @@ static int sctp_v6_is_ce(const struct sk_buff *skb) /* Dump the v6 addr to the seq file. */ static void sctp_v6_seq_dump_addr(struct seq_file *seq, union sctp_addr *addr) { - seq_printf(seq, "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ", - NIP6(addr->v6.sin6_addr)); + seq_printf(seq, NIP6_FMT " ", NIP6(addr->v6.sin6_addr)); } /* Initialize a PF_INET6 socket msg_name. */ @@ -755,7 +755,7 @@ static void sctp_inet6_skb_msgname(struct sk_buff *skb, char *msgname, } /* Do we support this AF? */ -static int sctp_inet6_af_supported(sa_family_t family, struct sctp_opt *sp) +static int sctp_inet6_af_supported(sa_family_t family, struct sctp_sock *sp) { switch (family) { case AF_INET6: @@ -775,7 +775,7 @@ static int sctp_inet6_af_supported(sa_family_t family, struct sctp_opt *sp) */ static int sctp_inet6_cmp_addr(const union sctp_addr *addr1, const union sctp_addr *addr2, - struct sctp_opt *opt) + struct sctp_sock *opt) { struct sctp_af *af1, *af2; @@ -797,7 +797,7 @@ static int sctp_inet6_cmp_addr(const union sctp_addr *addr1, /* Verify that the provided sockaddr looks bindable. Common verification, * has already been taken care of. */ -static int sctp_inet6_bind_verify(struct sctp_opt *opt, union sctp_addr *addr) +static int sctp_inet6_bind_verify(struct sctp_sock *opt, union sctp_addr *addr) { struct sctp_af *af; @@ -805,29 +805,26 @@ static int sctp_inet6_bind_verify(struct sctp_opt *opt, union sctp_addr *addr) if (addr->sa.sa_family != AF_INET6) af = sctp_get_af_specific(addr->sa.sa_family); else { - struct sock *sk; int type = ipv6_addr_type(&addr->v6.sin6_addr); - sk = sctp_opt2sk(opt); + struct net_device *dev; + if (type & IPV6_ADDR_LINKLOCAL) { - /* Note: Behavior similar to af_inet6.c: - * 1) Overrides previous bound_dev_if - * 2) Destructive even if bind isn't successful. - */ - - if (addr->v6.sin6_scope_id) - sk->sk_bound_dev_if = addr->v6.sin6_scope_id; - if (!sk->sk_bound_dev_if) + if (!addr->v6.sin6_scope_id) return 0; + dev = dev_get_by_index(addr->v6.sin6_scope_id); + if (!dev) + return 0; + dev_put(dev); } af = opt->pf->af; } return af->available(addr, opt); } -/* Verify that the provided sockaddr looks bindable. Common verification, +/* Verify that the provided sockaddr looks sendable. Common verification, * has already been taken care of. */ -static int sctp_inet6_send_verify(struct sctp_opt *opt, union sctp_addr *addr) +static int sctp_inet6_send_verify(struct sctp_sock *opt, union sctp_addr *addr) { struct sctp_af *af = NULL; @@ -835,19 +832,16 @@ static int sctp_inet6_send_verify(struct sctp_opt *opt, union sctp_addr *addr) if (addr->sa.sa_family != AF_INET6) af = sctp_get_af_specific(addr->sa.sa_family); else { - struct sock *sk; int type = ipv6_addr_type(&addr->v6.sin6_addr); - sk = sctp_opt2sk(opt); + struct net_device *dev; + if (type & IPV6_ADDR_LINKLOCAL) { - /* Note: Behavior similar to af_inet6.c: - * 1) Overrides previous bound_dev_if - * 2) Destructive even if bind isn't successful. - */ - - if (addr->v6.sin6_scope_id) - sk->sk_bound_dev_if = addr->v6.sin6_scope_id; - if (!sk->sk_bound_dev_if) + if (!addr->v6.sin6_scope_id) + return 0; + dev = dev_get_by_index(addr->v6.sin6_scope_id); + if (!dev) return 0; + dev_put(dev); } af = opt->pf->af; } @@ -861,7 +855,7 @@ static int sctp_inet6_send_verify(struct sctp_opt *opt, union sctp_addr *addr) * addresses. * Returns number of addresses supported. */ -static int sctp_inet6_supported_addrs(const struct sctp_opt *opt, +static int sctp_inet6_supported_addrs(const struct sctp_sock *opt, __u16 *types) { types[0] = SCTP_PARAM_IPV4_ADDRESS; @@ -869,30 +863,34 @@ static int sctp_inet6_supported_addrs(const struct sctp_opt *opt, return 2; } -static struct proto_ops inet6_seqpacket_ops = { - .family = PF_INET6, - .owner = THIS_MODULE, - .release = inet6_release, - .bind = inet6_bind, - .connect = inet_dgram_connect, - .socketpair = sock_no_socketpair, - .accept = inet_accept, - .getname = inet6_getname, - .poll = sctp_poll, - .ioctl = inet6_ioctl, - .listen = sctp_inet_listen, - .shutdown = inet_shutdown, - .setsockopt = inet_setsockopt, - .getsockopt = inet_getsockopt, - .sendmsg = inet_sendmsg, - .recvmsg = inet_recvmsg, - .mmap = sock_no_mmap, +static const struct proto_ops inet6_seqpacket_ops = { + .family = PF_INET6, + .owner = THIS_MODULE, + .release = inet6_release, + .bind = inet6_bind, + .connect = inet_dgram_connect, + .socketpair = sock_no_socketpair, + .accept = inet_accept, + .getname = inet6_getname, + .poll = sctp_poll, + .ioctl = inet6_ioctl, + .listen = sctp_inet_listen, + .shutdown = inet_shutdown, + .setsockopt = sock_common_setsockopt, + .getsockopt = sock_common_getsockopt, + .sendmsg = inet_sendmsg, + .recvmsg = sock_common_recvmsg, + .mmap = sock_no_mmap, +#ifdef CONFIG_COMPAT + .compat_setsockopt = compat_sock_common_setsockopt, + .compat_getsockopt = compat_sock_common_getsockopt, +#endif }; static struct inet_protosw sctpv6_seqpacket_protosw = { .type = SOCK_SEQPACKET, .protocol = IPPROTO_SCTP, - .prot = &sctp_prot, + .prot = &sctpv6_prot, .ops = &inet6_seqpacket_ops, .capability = -1, .no_check = 0, @@ -901,14 +899,14 @@ static struct inet_protosw sctpv6_seqpacket_protosw = { static struct inet_protosw sctpv6_stream_protosw = { .type = SOCK_STREAM, .protocol = IPPROTO_SCTP, - .prot = &sctp_prot, + .prot = &sctpv6_prot, .ops = &inet6_seqpacket_ops, .capability = -1, .no_check = 0, .flags = SCTP_PROTOSW_FLAG, }; -static int sctp6_rcv(struct sk_buff **pskb, unsigned int *nhoffp) +static int sctp6_rcv(struct sk_buff **pskb) { return sctp_rcv(*pskb) ? -1 : 0; } @@ -920,31 +918,35 @@ static struct inet6_protocol sctpv6_protocol = { }; static struct sctp_af sctp_ipv6_specific = { - .sctp_xmit = sctp_v6_xmit, - .setsockopt = ipv6_setsockopt, - .getsockopt = ipv6_getsockopt, - .get_dst = sctp_v6_get_dst, - .get_saddr = sctp_v6_get_saddr, - .copy_addrlist = sctp_v6_copy_addrlist, - .from_skb = sctp_v6_from_skb, - .from_sk = sctp_v6_from_sk, - .to_sk_saddr = sctp_v6_to_sk_saddr, - .to_sk_daddr = sctp_v6_to_sk_daddr, - .from_addr_param = sctp_v6_from_addr_param, - .to_addr_param = sctp_v6_to_addr_param, - .dst_saddr = sctp_v6_dst_saddr, - .cmp_addr = sctp_v6_cmp_addr, - .scope = sctp_v6_scope, - .addr_valid = sctp_v6_addr_valid, - .inaddr_any = sctp_v6_inaddr_any, - .is_any = sctp_v6_is_any, - .available = sctp_v6_available, - .skb_iif = sctp_v6_skb_iif, - .is_ce = sctp_v6_is_ce, - .seq_dump_addr = sctp_v6_seq_dump_addr, - .net_header_len = sizeof(struct ipv6hdr), - .sockaddr_len = sizeof(struct sockaddr_in6), - .sa_family = AF_INET6, + .sa_family = AF_INET6, + .sctp_xmit = sctp_v6_xmit, + .setsockopt = ipv6_setsockopt, + .getsockopt = ipv6_getsockopt, + .get_dst = sctp_v6_get_dst, + .get_saddr = sctp_v6_get_saddr, + .copy_addrlist = sctp_v6_copy_addrlist, + .from_skb = sctp_v6_from_skb, + .from_sk = sctp_v6_from_sk, + .to_sk_saddr = sctp_v6_to_sk_saddr, + .to_sk_daddr = sctp_v6_to_sk_daddr, + .from_addr_param = sctp_v6_from_addr_param, + .to_addr_param = sctp_v6_to_addr_param, + .dst_saddr = sctp_v6_dst_saddr, + .cmp_addr = sctp_v6_cmp_addr, + .scope = sctp_v6_scope, + .addr_valid = sctp_v6_addr_valid, + .inaddr_any = sctp_v6_inaddr_any, + .is_any = sctp_v6_is_any, + .available = sctp_v6_available, + .skb_iif = sctp_v6_skb_iif, + .is_ce = sctp_v6_is_ce, + .seq_dump_addr = sctp_v6_seq_dump_addr, + .net_header_len = sizeof(struct ipv6hdr), + .sockaddr_len = sizeof(struct sockaddr_in6), +#ifdef CONFIG_COMPAT + .compat_setsockopt = compat_ipv6_setsockopt, + .compat_getsockopt = compat_ipv6_getsockopt, +#endif }; static struct sctp_pf sctp_pf_inet6_specific = { @@ -963,9 +965,14 @@ static struct sctp_pf sctp_pf_inet6_specific = { /* Initialize IPv6 support and register with inet6 stack. */ int sctp_v6_init(void) { + int rc = proto_register(&sctpv6_prot, 1); + + if (rc) + goto out; /* Register inet6 protocol. */ + rc = -EAGAIN; if (inet6_add_protocol(&sctpv6_protocol, IPPROTO_SCTP) < 0) - return -EAGAIN; + goto out_unregister_sctp_proto; /* Add SCTPv6(UDP and TCP style) to inetsw6 linked list. */ inet6_register_protosw(&sctpv6_seqpacket_protosw); @@ -978,9 +985,13 @@ int sctp_v6_init(void) sctp_register_af(&sctp_ipv6_specific); /* Register notifier for inet6 address additions/deletions. */ - register_inet6addr_notifier(&sctp_inetaddr_notifier); - - return 0; + register_inet6addr_notifier(&sctp_inet6addr_notifier); + rc = 0; +out: + return rc; +out_unregister_sctp_proto: + proto_unregister(&sctpv6_prot); + goto out; } /* IPv6 specific exit support. */ @@ -990,5 +1001,6 @@ void sctp_v6_exit(void) inet6_del_protocol(&sctpv6_protocol, IPPROTO_SCTP); inet6_unregister_protosw(&sctpv6_seqpacket_protosw); inet6_unregister_protosw(&sctpv6_stream_protosw); - unregister_inet6addr_notifier(&sctp_inetaddr_notifier); + unregister_inet6addr_notifier(&sctp_inet6addr_notifier); + proto_unregister(&sctpv6_prot); }