diff options
| -rw-r--r-- | include/net/sctp/sctp.h | 4 | ||||
| -rw-r--r-- | include/net/sctp/structs.h | 21 | ||||
| -rw-r--r-- | net/sctp/associola.c | 2 | ||||
| -rw-r--r-- | net/sctp/bind_addr.c | 4 | ||||
| -rw-r--r-- | net/sctp/input.c | 2 | ||||
| -rw-r--r-- | net/sctp/ipv6.c | 85 | ||||
| -rw-r--r-- | net/sctp/protocol.c | 126 | ||||
| -rw-r--r-- | net/sctp/sm_sideeffect.c | 28 | ||||
| -rw-r--r-- | net/sctp/socket.c | 302 | ||||
| -rw-r--r-- | net/sctp/transport.c | 2 |
10 files changed, 295 insertions, 281 deletions
diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index 73c948813e72..f12e54cd4919 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -123,8 +123,8 @@ extern sctp_protocol_t sctp_proto; extern struct sock *sctp_get_ctl_sock(void); extern int sctp_copy_local_addr_list(sctp_protocol_t *, sctp_bind_addr_t *, sctp_scope_t, int priority, int flags); -extern sctp_pf_t *sctp_get_pf_specific(int family); -extern void sctp_set_pf_specific(int family, sctp_pf_t *); +extern struct sctp_pf *sctp_get_pf_specific(sa_family_t family); +extern int sctp_register_pf(struct sctp_pf *, sa_family_t); /* * sctp_socket.c diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index d7b5674bdbdc..4b7f0e451449 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -234,7 +234,7 @@ struct SCTP_protocol { * Pointers to address related SCTP functions. * (i.e. things that depend on the address family.) */ -typedef struct sctp_func { +struct sctp_af { int (*queue_xmit) (struct sk_buff *skb); int (*setsockopt) (struct sock *sk, int level, @@ -259,27 +259,34 @@ typedef struct sctp_func { void (*from_skb) (union sctp_addr *, struct sk_buff *skb, int saddr); + void (*from_sk) (union sctp_addr *, + struct sock *sk); + void (*to_sk) (union sctp_addr *, + struct sock *sk); int (*addr_valid) (union sctp_addr *); sctp_scope_t (*scope) (union sctp_addr *); void (*inaddr_any) (union sctp_addr *, unsigned short); int (*is_any) (const union sctp_addr *); + int (*available) (const union sctp_addr *); __u16 net_header_len; int sockaddr_len; sa_family_t sa_family; struct list_head list; -} sctp_func_t; +}; -sctp_func_t *sctp_get_af_specific(sa_family_t); +struct sctp_af *sctp_get_af_specific(sa_family_t); +int sctp_register_af(struct sctp_af *); /* Protocol family functions. */ typedef struct sctp_pf { void (*event_msgname)(sctp_ulpevent_t *, char *, int *); - void (*skb_msgname)(struct sk_buff *, char *, int *); - int (*af_supported)(sa_family_t); + void (*skb_msgname) (struct sk_buff *, char *, int *); + int (*af_supported) (sa_family_t); int (*cmp_addr) (const union sctp_addr *, const union sctp_addr *, struct sctp_opt *); - struct sctp_func *af; + int (*bind_verify) (struct sctp_opt *, union sctp_addr *); + struct sctp_af *af; } sctp_pf_t; /* SCTP Socket type: UDP or TCP style. */ @@ -623,7 +630,7 @@ struct SCTP_transport { union sctp_addr ipaddr; /* These are the functions we call to handle LLP stuff. */ - sctp_func_t *af_specific; + struct sctp_af *af_specific; /* Which association do we belong to? */ sctp_association_t *asoc; diff --git a/net/sctp/associola.c b/net/sctp/associola.c index 7736677f2d22..17a45a7f6932 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c @@ -642,7 +642,7 @@ __u16 __sctp_association_get_next_ssn(sctp_association_t *asoc, __u16 sid) int sctp_cmp_addr_exact(const union sctp_addr *ss1, const union sctp_addr *ss2) { - struct sctp_func *af; + struct sctp_af *af; af = sctp_get_af_specific(ss1->sa.sa_family); if (!af) diff --git a/net/sctp/bind_addr.c b/net/sctp/bind_addr.c index 7a78fdb5d7ff..2ae655f2c775 100644 --- a/net/sctp/bind_addr.c +++ b/net/sctp/bind_addr.c @@ -327,7 +327,7 @@ static int sctp_copy_one_addr(sctp_bind_addr_t *dest, union sctp_addr *addr, /* Is this a wildcard address? */ int sctp_is_any(const union sctp_addr *addr) { - struct sctp_func *af = sctp_get_af_specific(addr->sa.sa_family); + struct sctp_af *af = sctp_get_af_specific(addr->sa.sa_family); if (!af) return 0; return af->is_any(addr); @@ -362,7 +362,7 @@ int sctp_in_scope(const union sctp_addr *addr, sctp_scope_t scope) /* What is the scope of 'addr'? */ sctp_scope_t sctp_scope(const union sctp_addr *addr) { - struct sctp_func *af; + struct sctp_af *af; af = sctp_get_af_specific(addr->sa.sa_family); if (!af) diff --git a/net/sctp/input.c b/net/sctp/input.c index cf9e54b99283..dfec562150ef 100644 --- a/net/sctp/input.c +++ b/net/sctp/input.c @@ -96,7 +96,7 @@ int sctp_rcv(struct sk_buff *skb) struct sctphdr *sh; union sctp_addr src; union sctp_addr dest; - struct sctp_func *af; + struct sctp_af *af; int ret = 0; if (skb->pkt_type!=PACKET_HOST) diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c index 1391167e9709..958b4c60ed77 100644 --- a/net/sctp/ipv6.c +++ b/net/sctp/ipv6.c @@ -140,8 +140,8 @@ static inline int sctp_v6_xmit(struct sk_buff *skb) * to disallow loopback when the scoping rules have * not bound loopback to the endpoint. */ - if (sctp_ipv6_addr_type(&saddr) & IPV6_ADDR_LOOPBACK) { - if (!(sctp_ipv6_addr_type(&np->daddr) & + if (ipv6_addr_type(&saddr) & IPV6_ADDR_LOOPBACK) { + if (!(ipv6_addr_type(&np->daddr) & IPV6_ADDR_LOOPBACK)) { ipv6_addr_copy(&saddr, &np->daddr); } @@ -176,7 +176,7 @@ struct dst_entry *sctp_v6_get_dst(union sctp_addr *daddr, union sctp_addr *saddr) { struct dst_entry *dst; - struct flowi fl = { + struct flowi fl = { .nl_u = { .ip6_u = { .daddr = &daddr->v6.sin6_addr, } } }; @@ -261,6 +261,20 @@ static void sctp_v6_from_skb(union sctp_addr *addr,struct sk_buff *skb, ipv6_addr_copy(&addr->v6.sin6_addr, from); } +/* Initialize an sctp_addr from a socket. */ +static void sctp_v6_from_sk(union sctp_addr *addr, struct sock *sk) +{ + addr->v6.sin6_family = AF_INET6; + addr->v6.sin6_port = inet_sk(sk)->num; + addr->v6.sin6_addr = inet6_sk(sk)->rcv_saddr; +} + +/* Initialize sk->rcv_saddr from sctp_addr. */ +static void sctp_v6_to_sk(union sctp_addr *addr, struct sock *sk) +{ + inet6_sk(sk)->rcv_saddr = addr->v6.sin6_addr; +} + /* Initialize a sctp_addr from a dst_entry. */ static void sctp_v6_dst_saddr(union sctp_addr *addr, struct dst_entry *dst) { @@ -270,15 +284,15 @@ static void sctp_v6_dst_saddr(union sctp_addr *addr, struct dst_entry *dst) } /* Compare addresses exactly. Well.. almost exactly; ignore scope_id - * for now. FIXME. + * for now. FIXME: v4-mapped-v6. */ -static int sctp_v6_cmp_addr(const union sctp_addr *addr1, +static int sctp_v6_cmp_addr(const union sctp_addr *addr1, const union sctp_addr *addr2) { int match; if (addr1->sa.sa_family != addr2->sa.sa_family) return 0; - match = !ipv6_addr_cmp((struct in6_addr *)&addr1->v6.sin6_addr, + match = !ipv6_addr_cmp((struct in6_addr *)&addr1->v6.sin6_addr, (struct in6_addr *)&addr2->v6.sin6_addr); return match; @@ -300,6 +314,22 @@ static int sctp_v6_is_any(const union sctp_addr *addr) return IPV6_ADDR_ANY == type; } +/* Should this be available for binding? */ +static int sctp_v6_available(const union sctp_addr *addr) +{ + int type; + struct in6_addr *in6 = (struct in6_addr *)&addr->v6.sin6_addr; + + type = ipv6_addr_type(in6); + if (IPV6_ADDR_ANY == type) + return 1; + if (!(type & IPV6_ADDR_UNICAST)) + return 0; + + return ipv6_chk_addr(in6, NULL); +} + + /* This function checks if the address is a valid address to be used for * SCTP. * @@ -309,7 +339,7 @@ static int sctp_v6_is_any(const union sctp_addr *addr) */ static int sctp_v6_addr_valid(union sctp_addr *addr) { - int ret = sctp_ipv6_addr_type(&addr->v6.sin6_addr); + int ret = ipv6_addr_type(&addr->v6.sin6_addr); /* FIXME: v4-mapped-v6 address support. */ @@ -442,14 +472,14 @@ static int sctp_inet6_af_supported(sa_family_t family) /* Address matching with wildcards allowed. This extra level * of indirection lets us choose whether a PF_INET6 should - * disallow any v4 addresses if we so choose. + * disallow any v4 addresses if we so choose. */ -static int sctp_inet6_cmp_addr(const union sctp_addr *addr1, +static int sctp_inet6_cmp_addr(const union sctp_addr *addr1, const union sctp_addr *addr2, struct sctp_opt *opt) { - struct sctp_func *af1, *af2; - + struct sctp_af *af1, *af2; + af1 = sctp_get_af_specific(addr1->sa.sa_family); af2 = sctp_get_af_specific(addr2->sa.sa_family); @@ -461,11 +491,25 @@ static int sctp_inet6_cmp_addr(const union sctp_addr *addr1, if (addr1->sa.sa_family != addr2->sa.sa_family) return 0; - + return af1->cmp_addr(addr1, addr2); } +/* 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) +{ + struct sctp_af *af; + /* ASSERT: address family has already been verified. */ + if (addr->sa.sa_family != AF_INET6) { + af = sctp_get_af_specific(addr->sa.sa_family); + } else + af = opt->pf->af; + + return af->available(addr); +} static struct proto_ops inet6_seqpacket_ops = { .family = PF_INET6, @@ -501,29 +545,33 @@ static struct inet6_protocol sctpv6_protocol = { .err_handler = sctp_v6_err, }; -static sctp_func_t sctp_ipv6_specific = { +static struct sctp_af sctp_ipv6_specific = { .queue_xmit = sctp_v6_xmit, - .setsockopt = ipv6_setsockopt, + .setsockopt = ipv6_setsockopt, .getsockopt = ipv6_getsockopt, .get_dst = sctp_v6_get_dst, .copy_addrlist = sctp_v6_copy_addrlist, .from_skb = sctp_v6_from_skb, + .from_sk = sctp_v6_from_sk, + .to_sk = sctp_v6_to_sk, .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, .net_header_len = sizeof(struct ipv6hdr), .sockaddr_len = sizeof(struct sockaddr_in6), .sa_family = AF_INET6, }; -static sctp_pf_t sctp_pf_inet6_specific = { +static struct sctp_pf sctp_pf_inet6_specific = { .event_msgname = sctp_inet6_event_msgname, .skb_msgname = sctp_inet6_skb_msgname, .af_supported = sctp_inet6_af_supported, .cmp_addr = sctp_inet6_cmp_addr, + .bind_verify = sctp_inet6_bind_verify, .af = &sctp_ipv6_specific, }; @@ -538,11 +586,10 @@ int sctp_v6_init(void) inet6_register_protosw(&sctpv6_protosw); /* Register the SCTP specfic PF_INET6 functions. */ - sctp_set_pf_specific(PF_INET6, &sctp_pf_inet6_specific); + sctp_register_pf(&sctp_pf_inet6_specific, PF_INET6); - /* Fill in address family info. */ - INIT_LIST_HEAD(&sctp_ipv6_specific.list); - list_add_tail(&sctp_ipv6_specific.list, &sctp_proto.address_families); + /* Register the SCTP specfic AF_INET6 functions. */ + sctp_register_af(&sctp_ipv6_specific); return 0; } diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index cc45fe85c46d..417d1d9eab07 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -67,8 +67,10 @@ struct sctp_mib sctp_statistics[NR_CPUS * 2]; */ static struct socket *sctp_ctl_socket; -static sctp_pf_t *sctp_pf_inet6_specific; -static sctp_pf_t *sctp_pf_inet_specific; +static struct sctp_pf *sctp_pf_inet6_specific; +static struct sctp_pf *sctp_pf_inet_specific; +static struct sctp_af *sctp_af_v4_specific; +static struct sctp_af *sctp_af_v6_specific; extern struct net_proto_family inet_family_ops; @@ -140,12 +142,12 @@ static void __sctp_get_local_addr_list(sctp_protocol_t *proto) { struct net_device *dev; struct list_head *pos; - struct sctp_func *af; + struct sctp_af *af; read_lock(&dev_base_lock); for (dev = dev_base; dev; dev = dev->next) { list_for_each(pos, &proto->address_families) { - af = list_entry(pos, sctp_func_t, list); + af = list_entry(pos, struct sctp_af, list); af->copy_addrlist(&proto->local_addr_list, dev); } } @@ -251,7 +253,6 @@ struct dst_entry *sctp_v4_get_dst(union sctp_addr *daddr, return &rt->u.dst; } - /* Initialize a sctp_addr from in incoming skb. */ static void sctp_v4_from_skb(union sctp_addr *addr, struct sk_buff *skb, int is_saddr) @@ -274,6 +275,21 @@ static void sctp_v4_from_skb(union sctp_addr *addr, struct sk_buff *skb, memcpy(&addr->v4.sin_addr.s_addr, from, sizeof(struct in_addr)); } +/* Initialize an sctp_addr from a socket. */ +static void sctp_v4_from_sk(union sctp_addr *addr, struct sock *sk) +{ + addr->v4.sin_family = AF_INET; + addr->v4.sin_port = inet_sk(sk)->num; + addr->v4.sin_addr.s_addr = inet_sk(sk)->rcv_saddr; +} + +/* Initialize sk->rcv_saddr from sctp_addr. */ +static void sctp_v4_to_sk(union sctp_addr *addr, struct sock *sk) +{ + inet_sk(sk)->rcv_saddr = addr->v4.sin_addr.s_addr; +} + + /* Initialize a sctp_addr from a dst_entry. */ static void sctp_v4_dst_saddr(union sctp_addr *saddr, struct dst_entry *dst) { @@ -311,7 +327,7 @@ static int sctp_v4_is_any(const union sctp_addr *addr) } /* This function checks if the address is a valid address to be used for - * SCTP. + * SCTP binding. * * Output: * Return 0 - If the address is a non-unicast or an illegal address. @@ -326,6 +342,18 @@ static int sctp_v4_addr_valid(union sctp_addr *addr) return 1; } +/* Should this be available for binding? */ +static int sctp_v4_available(const union sctp_addr *addr) +{ + int ret = inet_addr_type(addr->v4.sin_addr.s_addr); + + /* FIXME: ip_nonlocal_bind sysctl support. */ + + if (addr->v4.sin_addr.s_addr != INADDR_ANY && ret != RTN_LOCAL) + return 0; + return 1; +} + /* Checking the loopback, private and other address scopes as defined in * RFC 1918. The IPv4 scoping is based on the draft for SCTP IPv4 * scoping <draft-stewart-tsvwg-sctp-ipv4-00.txt>. @@ -405,29 +433,42 @@ int sctp_ctl_sock_init(void) return 0; } +/* Register address family specific functions. */ +int sctp_register_af(struct sctp_af *af) +{ + switch (af->sa_family) { + case AF_INET: + if (sctp_af_v4_specific) + return 0; + sctp_af_v4_specific = sctp_af_v4_specific = af; + break; + case AF_INET6: + if (sctp_af_v6_specific) + return 0; + sctp_af_v6_specific = sctp_af_v6_specific = af; + break; + default: + return 0; + } + + INIT_LIST_HEAD(&af->list); + list_add_tail(&af->list, &sctp_proto.address_families); + return 1; +} + /* Get the table of functions for manipulating a particular address * family. */ -sctp_func_t *sctp_get_af_specific(sa_family_t family) +struct sctp_af *sctp_get_af_specific(sa_family_t family) { - struct list_head *pos; - sctp_protocol_t *proto = sctp_get_protocol(); - struct sctp_func *retval, *af; - - retval = NULL; - - /* Cycle through all AF specific functions looking for a - * match. - */ - list_for_each(pos, &proto->address_families) { - af = list_entry(pos, sctp_func_t, list); - if (family == af->sa_family) { - retval = af; - break; - } + switch (family) { + case AF_INET: + return sctp_af_v4_specific; + case AF_INET6: + return sctp_af_v6_specific; + default: + return NULL; } - - return retval; } /* Common code to initialize a AF_INET msg_name. */ @@ -495,18 +536,25 @@ static int sctp_inet_cmp_addr(const union sctp_addr *addr1, return 0; } +/* Verify that provided sockaddr looks bindable. Common verification has + * already been taken care of. + */ +static int sctp_inet_bind_verify(struct sctp_opt *opt, union sctp_addr *addr) +{ + return sctp_v4_available(addr); +} -struct sctp_func sctp_ipv4_specific; +struct sctp_af sctp_ipv4_specific; -static sctp_pf_t sctp_pf_inet = { +static struct sctp_pf sctp_pf_inet = { .event_msgname = sctp_inet_event_msgname, .skb_msgname = sctp_inet_skb_msgname, .af_supported = sctp_inet_af_supported, .cmp_addr = sctp_inet_cmp_addr, + .bind_verify = sctp_inet_bind_verify, .af = &sctp_ipv4_specific, }; - /* Registration for netdev events. */ struct notifier_block sctp_netdev_notifier = { .notifier_call = sctp_netdev_event, @@ -551,25 +599,28 @@ static struct inet_protocol sctp_protocol = { }; /* IPv4 address related functions. */ -struct sctp_func sctp_ipv4_specific = { +struct sctp_af sctp_ipv4_specific = { .queue_xmit = ip_queue_xmit, .setsockopt = ip_setsockopt, .getsockopt = ip_getsockopt, .get_dst = sctp_v4_get_dst, .copy_addrlist = sctp_v4_copy_addrlist, .from_skb = sctp_v4_from_skb, + .from_sk = sctp_v4_from_sk, + .to_sk = sctp_v4_to_sk, .dst_saddr = sctp_v4_dst_saddr, .cmp_addr = sctp_v4_cmp_addr, .addr_valid = sctp_v4_addr_valid, .inaddr_any = sctp_v4_inaddr_any, .is_any = sctp_v4_is_any, + .available = sctp_v4_available, .scope = sctp_v4_scope, .net_header_len = sizeof(struct iphdr), .sockaddr_len = sizeof(struct sockaddr_in), .sa_family = AF_INET, }; -sctp_pf_t *sctp_get_pf_specific(int family) { +struct sctp_pf *sctp_get_pf_specific(sa_family_t family) { switch (family) { case PF_INET: @@ -581,20 +632,24 @@ sctp_pf_t *sctp_get_pf_specific(int family) { } } -/* Set the PF specific function table. */ -void sctp_set_pf_specific(int family, sctp_pf_t *pf) +/* Register the PF specific function table. */ +int sctp_register_pf(struct sctp_pf *pf, sa_family_t family) { switch (family) { case PF_INET: + if (sctp_pf_inet_specific) + return 0; sctp_pf_inet_specific = pf; break; case PF_INET6: + if (sctp_pf_inet6_specific) + return 0; sctp_pf_inet6_specific = pf; break; default: - BUG(); - break; + return 0; } + return 1; } /* Initialize the universe into something sensible. */ @@ -617,7 +672,7 @@ int sctp_init(void) sctp_dbg_objcnt_init(); /* Initialize the SCTP specific PF functions. */ - sctp_set_pf_specific(PF_INET, &sctp_pf_inet); + sctp_register_pf(&sctp_pf_inet, PF_INET); /* * 14. Suggested SCTP Protocol Parameter Values */ @@ -709,8 +764,7 @@ int sctp_init(void) sctp_sysctl_register(); INIT_LIST_HEAD(&sctp_proto.address_families); - INIT_LIST_HEAD(&sctp_ipv4_specific.list); - list_add_tail(&sctp_ipv4_specific.list, &sctp_proto.address_families); + sctp_register_af(&sctp_ipv4_specific); status = sctp_v6_init(); if (status) diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c index 69ca9dccc364..a979d06c4825 100644 --- a/net/sctp/sm_sideeffect.c +++ b/net/sctp/sm_sideeffect.c @@ -68,7 +68,8 @@ static void sctp_do_8_2_transport_strike(sctp_association_t *asoc, sctp_transport_t *transport); static void sctp_cmd_init_failed(sctp_cmd_seq_t *, sctp_association_t *asoc); static void sctp_cmd_assoc_failed(sctp_cmd_seq_t *, sctp_association_t *asoc, - sctp_event_t event_type, sctp_chunk_t *chunk); + sctp_event_t event_type, sctp_subtype_t stype, + sctp_chunk_t *chunk); static int sctp_cmd_process_init(sctp_cmd_seq_t *, sctp_association_t *asoc, sctp_chunk_t *chunk, sctp_init_chunk_t *peer_init, @@ -517,7 +518,7 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype, case SCTP_CMD_ASSOC_FAILED: sctp_cmd_assoc_failed(commands, asoc, event_type, - chunk); + subtype, chunk); break; case SCTP_CMD_COUNTER_INC: @@ -1046,18 +1047,27 @@ static void sctp_cmd_init_failed(sctp_cmd_seq_t *commands, static void sctp_cmd_assoc_failed(sctp_cmd_seq_t *commands, sctp_association_t *asoc, sctp_event_t event_type, + sctp_subtype_t subtype, sctp_chunk_t *chunk) { sctp_ulpevent_t *event; __u16 error = 0; - if (event_type == SCTP_EVENT_T_PRIMITIVE) - error = SCTP_ERROR_USER_ABORT; - - if (chunk && (SCTP_CID_ABORT == chunk->chunk_hdr->type) && - (ntohs(chunk->chunk_hdr->length) >= (sizeof(struct sctp_chunkhdr) + - sizeof(struct sctp_errhdr)))) { - error = ((sctp_errhdr_t *)chunk->skb->data)->cause; + switch(event_type) { + case SCTP_EVENT_T_PRIMITIVE: + if (SCTP_PRIMITIVE_ABORT == subtype.primitive) + error = SCTP_ERROR_USER_ABORT; + break; + case SCTP_EVENT_T_CHUNK: + if (chunk && (SCTP_CID_ABORT == chunk->chunk_hdr->type) && + (ntohs(chunk->chunk_hdr->length) >= + (sizeof(struct sctp_chunkhdr) + + sizeof(struct sctp_errhdr)))) { + error = ((sctp_errhdr_t *)chunk->skb->data)->cause; + } + break; + default: + break; } event = sctp_ulpevent_make_assoc_change(asoc, diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 54e06e12395c..ec04da89c96b 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -87,12 +87,7 @@ static int sctp_wait_for_sndbuf(sctp_association_t *asoc, long *timeo_p, int msg_len); static int sctp_wait_for_packet(struct sock * sk, int *err, long *timeo_p); static int sctp_wait_for_connect(sctp_association_t *asoc, long *timeo_p); -static inline void sctp_sk_addr_set(struct sock *, - const union sctp_addr *newaddr, - union sctp_addr *saveaddr); -static inline void sctp_sk_addr_restore(struct sock *, - const union sctp_addr *); -static inline int sctp_verify_addr(struct sock *, struct sockaddr *, int); +static inline int sctp_verify_addr(struct sock *, union sctp_addr *, int); static int sctp_bindx_add(struct sock *, struct sockaddr_storage *, int); static int sctp_bindx_rem(struct sock *, struct sockaddr_storage *, int); static int sctp_do_bind(struct sock *, union sctp_addr *, int); @@ -133,101 +128,75 @@ int sctp_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len) return retval; } -static long sctp_get_port_local(struct sock *, unsigned short); +static long sctp_get_port_local(struct sock *, union sctp_addr *); -/* Bind a local address either to an endpoint or to an association. */ -SCTP_STATIC int sctp_do_bind(struct sock *sk, union sctp_addr *newaddr, - int addr_len) +/* Verify this is a valid sockaddr. */ +static struct sctp_af *sctp_sockaddr_af(struct sctp_opt *opt, + union sctp_addr *addr, int len) { - sctp_opt_t *sp = sctp_sk(sk); - sctp_endpoint_t *ep = sp->ep; - sctp_bind_addr_t *bp = &ep->base.bind_addr; - unsigned short sa_family = newaddr->sa.sa_family; - union sctp_addr tmpaddr, saveaddr; - unsigned short *snum; - int ret = 0; + struct sctp_af *af; - SCTP_DEBUG_PRINTK("sctp_do_bind(sk: %p, newaddr: %p, addr_len: %d)\n", - sk, newaddr, addr_len); + /* Check minimum size. */ + if (len < sizeof (struct sockaddr)) + return NULL; - /* FIXME: This function needs to handle v4-mapped-on-v6 - * addresses! - */ - if (PF_INET == sk->family) { - if (sa_family != AF_INET) - return -EINVAL; - } + /* Does this PF support this AF? */ + if (!opt->pf->af_supported(addr->sa.sa_family)) + return NULL; - /* Make a local copy of the new address. */ - tmpaddr = *newaddr; + /* If we get this far, af is valid. */ + af = sctp_get_af_specific(addr->sa.sa_family); - switch (sa_family) { - case AF_INET: - if (addr_len < sizeof(struct sockaddr_in)) - return -EINVAL; - - ret = inet_addr_type(newaddr->v4.sin_addr.s_addr); + if (len < af->sockaddr_len) + return NULL; - /* FIXME: - * Should we allow apps to bind to non-local addresses by - * checking the IP sysctl parameter "ip_nonlocal_bind"? - */ - if (newaddr->v4.sin_addr.s_addr != INADDR_ANY && - ret != RTN_LOCAL) - return -EADDRNOTAVAIL; + return af; +} - tmpaddr.v4.sin_port = htons(tmpaddr.v4.sin_port); - snum = &tmpaddr.v4.sin_port; - break; - case AF_INET6: - SCTP_V6( - /* FIXME: Hui, please verify this. Looking at - * the ipv6 code I see a SIN6_LEN_RFC2133 check. - * I'm guessing that scope_id is a newer addition. - */ - if (addr_len < sizeof(struct sockaddr_in6)) - return -EINVAL; +/* Bind a local address either to an endpoint or to an association. */ +SCTP_STATIC int sctp_do_bind(struct sock *sk, union sctp_addr *addr, int len) +{ + sctp_opt_t *sp = sctp_sk(sk); + sctp_endpoint_t *ep = sp->ep; + sctp_bind_addr_t *bp = &ep->base.bind_addr; + struct sctp_af *af; + unsigned short snum; + int ret = 0; - /* FIXME - The support for IPv6 multiple types - * of addresses need to be added later. - */ - ret = sctp_ipv6_addr_type(&newaddr->v6.sin6_addr); - tmpaddr.v6.sin6_port = htons(tmpaddr.v6.sin6_port); - snum = &tmpaddr.v6.sin6_port; - break; - ) + SCTP_DEBUG_PRINTK("sctp_do_bind(sk: %p, newaddr: %p, len: %d)\n", + sk, addr, len); - default: + /* Common sockaddr verification. */ + af = sctp_sockaddr_af(sp, addr, len); + if (!af) return -EINVAL; - }; + + /* PF specific bind() address verification. */ + if (!sp->pf->bind_verify(sp, addr)) + return -EADDRNOTAVAIL; + + snum= ntohs(addr->v4.sin_port); SCTP_DEBUG_PRINTK("sctp_do_bind: port: %d, new port: %d\n", - bp->port, *snum); + bp->port, snum); /* We must either be unbound, or bind to the same port. */ - if (bp->port && (*snum != bp->port)) { + if (bp->port && (snum != bp->port)) { SCTP_DEBUG_PRINTK("sctp_do_bind:" " New port %d does not match existing port " - "%d.\n", *snum, bp->port); + "%d.\n", snum, bp->port); return -EINVAL; } - if (*snum && *snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE)) + if (snum && snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE)) return -EACCES; - /* FIXME - Make socket understand that there might be multiple bind - * addresses and there will be multiple source addresses involved in - * routing and failover decisions. - */ - sctp_sk_addr_set(sk, &tmpaddr, &saveaddr); - /* Make sure we are allowed to bind here. * The function sctp_get_port_local() does duplicate address * detection. */ - if ((ret = sctp_get_port_local(sk, *snum))) { - sctp_sk_addr_restore(sk, &saveaddr); + if ((ret = sctp_get_port_local(sk, addr))) { if (ret == (long) sk) { /* This endpoint has a conflicting address. */ return -EINVAL; @@ -237,25 +206,32 @@ SCTP_STATIC int sctp_do_bind(struct sock *sk, union sctp_addr *newaddr, } /* Refresh ephemeral port. */ - if (!*snum) - *snum = inet_sk(sk)->num; + if (!snum) + snum = inet_sk(sk)->num; + + - /* The getsockname() API depends on 'sport' being set. */ - inet_sk(sk)->sport = htons(inet_sk(sk)->num); /* Add the address to the bind address list. */ sctp_local_bh_disable(); sctp_write_lock(&ep->base.addr_lock); /* Use GFP_ATOMIC since BHs are disabled. */ - if ((ret = sctp_add_bind_addr(bp, &tmpaddr, GFP_ATOMIC))) { - sctp_sk_addr_restore(sk, &saveaddr); - } else if (!bp->port) { - bp->port = *snum; - } + addr->v4.sin_port = ntohs(addr->v4.sin_port); + ret = sctp_add_bind_addr(bp, addr, GFP_ATOMIC); + addr->v4.sin_port = htons(addr->v4.sin_port); + if (!ret && !bp->port) + bp->port = snum; sctp_write_unlock(&ep->base.addr_lock); sctp_local_bh_enable(); + + /* Copy back into socket for getsockname() use. */ + if (!ret) { + inet_sk(sk)->sport = htons(inet_sk(sk)->num); + af->to_sk(addr, sk); + } + return ret; } @@ -778,7 +754,7 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, * For a peeled-off socket, msg_name is ignored. */ if ((SCTP_SOCKET_UDP_HIGH_BANDWIDTH != sp->type) && msg->msg_name) { - err = sctp_verify_addr(sk, (struct sockaddr *)msg->msg_name, + err = sctp_verify_addr(sk, (union sctp_addr *)msg->msg_name, msg->msg_namelen); if (err) return err; @@ -1216,9 +1192,11 @@ SCTP_STATIC int sctp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr * Otherwise, set MSG_EOR indicating the end of a message. */ if (skb_len > copied) { + msg->msg_flags &= ~MSG_EOR; + if (flags & MSG_PEEK) + goto out_free; sctp_skb_pull(skb, copied); skb_queue_head(&sk->receive_queue, skb); - msg->msg_flags &= ~MSG_EOR; goto out; } else { msg->msg_flags |= MSG_EOR; @@ -1355,13 +1333,10 @@ static inline int sctp_setsockopt_set_peer_addr_params(struct sock *sk, * optlen - the size of the buffer. */ SCTP_STATIC int sctp_setsockopt(struct sock *sk, int level, int optname, - char *optval, int optlen) + char *optval, int optlen) { int retval = 0; char *tmp; - sctp_protocol_t *proto = sctp_get_protocol(); - struct list_head *pos; - sctp_func_t *af; SCTP_DEBUG_PRINTK("sctp_setsockopt(sk: %p... optname: %d)\n", sk, optname); @@ -1373,14 +1348,9 @@ SCTP_STATIC int sctp_setsockopt(struct sock *sk, int level, int optname, * are at all well-founded. */ if (level != SOL_SCTP) { - list_for_each(pos, &proto->address_families) { - af = list_entry(pos, sctp_func_t, list); - - retval = af->setsockopt(sk, level, optname, optval, - optlen); - if (retval < 0) - goto out_nounlock; - } + struct sctp_af *af = sctp_sk(sk)->pf->af; + retval = af->setsockopt(sk, level, optname, optval, optlen); + goto out_nounlock; } sctp_lock_sock(sk); @@ -1484,7 +1454,7 @@ SCTP_STATIC int sctp_connect(struct sock *sk, struct sockaddr *uaddr, goto out_unlock; } - err = sctp_verify_addr(sk, uaddr, addr_len); + err = sctp_verify_addr(sk, (union sctp_addr *)uaddr, addr_len); if (err) goto out_unlock; @@ -1942,9 +1912,6 @@ SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname, char *optval, int *optlen) { int retval = 0; - sctp_protocol_t *proto = sctp_get_protocol(); - sctp_func_t *af; - struct list_head *pos; int len; SCTP_DEBUG_PRINTK("sctp_getsockopt(sk: %p, ...)\n", sk); @@ -1956,13 +1923,10 @@ SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname, * are at all well-founded. */ if (level != SOL_SCTP) { - list_for_each(pos, &proto->address_families) { - af = list_entry(pos, sctp_func_t, list); - retval = af->getsockopt(sk, level, optname, - optval, optlen); - if (retval < 0) - return retval; - } + struct sctp_af *af = sctp_sk(sk)->pf->af; + + retval = af->getsockopt(sk, level, optname, optval, optlen); + return retval; } if (get_user(len, optlen)) @@ -2030,12 +1994,17 @@ static void sctp_unhash(struct sock *sk) */ static sctp_bind_bucket_t *sctp_bucket_create(sctp_bind_hashbucket_t *head, unsigned short snum); -static long sctp_get_port_local(struct sock *sk, unsigned short snum) +static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr) { sctp_bind_hashbucket_t *head; /* hash list */ sctp_bind_bucket_t *pp; /* hash list port iterator */ sctp_protocol_t *sctp = sctp_get_protocol(); + unsigned short snum; int ret; + + /* NOTE: Remember to put this back to net order. */ + addr->v4.sin_port = ntohs(addr->v4.sin_port); + snum = addr->v4.sin_port; SCTP_DEBUG_PRINTK("sctp_get_port() begins, snum=%d\n", snum); @@ -2101,6 +2070,7 @@ static long sctp_get_port_local(struct sock *sk, unsigned short snum) } } + if (pp != NULL && pp->sk != NULL) { /* We had a port hash table hit - there is an * available port (pp != NULL) and it is being @@ -2108,7 +2078,6 @@ static long sctp_get_port_local(struct sock *sk, unsigned short snum) * socket is going to be sk2. */ int sk_reuse = sk->reuse; - union sctp_addr tmpaddr; struct sock *sk2 = pp->sk; SCTP_DEBUG_PRINTK("sctp_get_port() found a " @@ -2116,27 +2085,6 @@ static long sctp_get_port_local(struct sock *sk, unsigned short snum) if (pp->fastreuse != 0 && sk->reuse != 0) goto success; - /* FIXME - multiple addresses need to be supported - * later. - */ - switch (sk->family) { - case PF_INET: - tmpaddr.v4.sin_family = AF_INET; - tmpaddr.v4.sin_port = snum; - tmpaddr.v4.sin_addr.s_addr = inet_sk(sk)->rcv_saddr; - break; - - case PF_INET6: - SCTP_V6(tmpaddr.v6.sin6_family = AF_INET6; - tmpaddr.v6.sin6_port = snum; - tmpaddr.v6.sin6_addr = inet6_sk(sk)->rcv_saddr; - ) - break; - - default: - break; - }; - /* Run through the list of sockets bound to the port * (pp->port) [via the pointers bind_next and * bind_pprev in the struct sock *sk2 (pp->sk)]. On each one, @@ -2154,8 +2102,7 @@ static long sctp_get_port_local(struct sock *sk, unsigned short snum) if (sk_reuse && sk2->reuse) continue; - if (sctp_bind_addr_match(&ep2->base.bind_addr, - &tmpaddr, + if (sctp_bind_addr_match(&ep2->base.bind_addr, addr, sctp_sk(sk))) goto found; } @@ -2207,12 +2154,25 @@ fail: sctp_local_bh_enable(); SCTP_DEBUG_PRINTK("sctp_get_port() ends, ret=%d\n", ret); + addr->v4.sin_port = htons(addr->v4.sin_port); return ret; } +/* Assign a 'snum' port to the socket. If snum == 0, an ephemeral + * port is requested. + */ static int sctp_get_port(struct sock *sk, unsigned short snum) { - long ret = sctp_get_port_local(sk, snum); + long ret; + union sctp_addr addr; + struct sctp_af *af = sctp_sk(sk)->pf->af; + + /* Set up a dummy address struct from the sk. */ + af->from_sk(&addr, sk); + addr.v4.sin_port = htons(snum); + + /* Note: sk->num gets filled in if ephemeral port request. */ + ret = sctp_get_port_local(sk, &addr); return (ret ? 1 : 0); } @@ -2413,7 +2373,7 @@ void sctp_put_port(struct sock *sk) static int sctp_autobind(struct sock *sk) { union sctp_addr autoaddr; - struct sctp_func *af; + struct sctp_af *af; unsigned short port; /* Initialize a local sockaddr structure to INADDR_ANY. */ @@ -2537,58 +2497,6 @@ SCTP_STATIC int sctp_msghdr_parse(const struct msghdr *msg, return 0; } -/* Setup sk->rcv_saddr before calling get_port(). */ -static inline void sctp_sk_addr_set(struct sock *sk, - const union sctp_addr *newaddr, - union sctp_addr *saveaddr) -{ - struct inet_opt *inet = inet_sk(sk); - - saveaddr->sa.sa_family = newaddr->sa.sa_family; - - switch (newaddr->sa.sa_family) { - case AF_INET: - saveaddr->v4.sin_addr.s_addr = inet->rcv_saddr; - inet->rcv_saddr = inet->saddr = newaddr->v4.sin_addr.s_addr; - break; - - case AF_INET6: - SCTP_V6({ - struct ipv6_pinfo *np = inet6_sk(sk); - - saveaddr->v6.sin6_addr = np->rcv_saddr; - np->rcv_saddr = np->saddr = newaddr->v6.sin6_addr; - break; - }) - - default: - break; - }; -} - -/* Restore sk->rcv_saddr after failing get_port(). */ -static inline void sctp_sk_addr_restore(struct sock *sk, const union sctp_addr *addr) -{ - struct inet_opt *inet = inet_sk(sk); - - switch (addr->sa.sa_family) { - case AF_INET: - inet->rcv_saddr = inet->saddr = addr->v4.sin_addr.s_addr; - break; - - case AF_INET6: - SCTP_V6({ - struct ipv6_pinfo *np = inet6_sk(sk); - - np->rcv_saddr = np->saddr = addr->v6.sin6_addr; - break; - }) - - default: - break; - }; -} - /* * Wait for a packet.. * Note: This function is the same function as in core/datagram.c @@ -2711,27 +2619,15 @@ no_packet: } /* Verify that this is a valid address. */ -static int sctp_verify_addr(struct sock *sk, struct sockaddr *addr, int len) +static int sctp_verify_addr(struct sock *sk, union sctp_addr *addr, int len) { - struct sctp_func *af; - - /* Check minimum size. */ - if (len < sizeof (struct sockaddr)) - return -EINVAL; + struct sctp_af *af; - /* Do we support this address family in general? */ - af = sctp_get_af_specific(addr->sa_family); + /* Verify basic sockaddr. */ + af = sctp_sockaddr_af(sctp_sk(sk), addr, len); if (!af) return -EINVAL; - /* Does this PF support this AF? */ - if (!sctp_sk(sk)->pf->af_supported(addr->sa_family)) - return -EINVAL; - - /* Verify the minimum for this AF sockaddr. */ - if (len < af->sockaddr_len) - return -EINVAL; - /* Is this a valid SCTP address? */ if (!af->addr_valid((union sctp_addr *)addr)) return -EINVAL; diff --git a/net/sctp/transport.c b/net/sctp/transport.c index 4fba07425349..303c33852b99 100644 --- a/net/sctp/transport.c +++ b/net/sctp/transport.c @@ -207,7 +207,7 @@ void sctp_transport_route(sctp_transport_t *transport, union sctp_addr *saddr, struct sctp_opt *opt) { sctp_association_t *asoc = transport->asoc; - struct sctp_func *af = transport->af_specific; + struct sctp_af *af = transport->af_specific; union sctp_addr *daddr = &transport->ipaddr; sctp_bind_addr_t *bp; rwlock_t *addr_lock; |
