Message ID | 20171017135806.4244-1-richard_c_haines@btinternet.com (mailing list archive) |
---|---|
State | Superseded |
Headers | show |
Hello, On Tue, Oct 17, 2017 at 02:58:06PM +0100, Richard Haines wrote: > Add ip option support to allow LSM security modules to utilise CIPSO/IPv4 > and CALIPSO/IPv6 services. > > Signed-off-by: Richard Haines <richard_c_haines@btinternet.com> > --- > include/net/sctp/structs.h | 2 ++ > net/sctp/chunk.c | 7 ++++--- > net/sctp/ipv6.c | 37 ++++++++++++++++++++++++++++++------- > net/sctp/output.c | 3 ++- > net/sctp/protocol.c | 36 ++++++++++++++++++++++++++++++++++++ > net/sctp/socket.c | 5 ++++- > 6 files changed, 78 insertions(+), 12 deletions(-) > > diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h > index 5ab29af..7767577 100644 > --- a/include/net/sctp/structs.h > +++ b/include/net/sctp/structs.h > @@ -461,6 +461,7 @@ struct sctp_af { > void (*ecn_capable)(struct sock *sk); > __u16 net_header_len; > int sockaddr_len; > + int (*ip_options_len)(struct sock *sk); > sa_family_t sa_family; > struct list_head list; > }; > @@ -485,6 +486,7 @@ struct sctp_pf { > int (*addr_to_user)(struct sctp_sock *sk, union sctp_addr *addr); > void (*to_sk_saddr)(union sctp_addr *, struct sock *sk); > void (*to_sk_daddr)(union sctp_addr *, struct sock *sk); > + void (*copy_ip_options)(struct sock *sk, struct sock *newsk); > struct sctp_af *af; > }; > > diff --git a/net/sctp/chunk.c b/net/sctp/chunk.c > index 1323d41..e49e240 100644 > --- a/net/sctp/chunk.c > +++ b/net/sctp/chunk.c > @@ -153,7 +153,6 @@ static void sctp_datamsg_assign(struct sctp_datamsg *msg, struct sctp_chunk *chu > chunk->msg = msg; > } > > - > /* A data chunk can have a maximum payload of (2^16 - 20). Break > * down any such message into smaller chunks. Opportunistically, fragment > * the chunks down to the current MTU constraints. We may get refragmented > @@ -190,7 +189,10 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc, > */ > max_data = asoc->pathmtu - > sctp_sk(asoc->base.sk)->pf->af->net_header_len - ^^^^^^^^ > - sizeof(struct sctphdr) - sizeof(struct sctp_data_chunk); > + sizeof(struct sctphdr) - sizeof(struct sctp_data_chunk) - > + sctp_sk(asoc->base.sk)->pf->af-> ^^^^^^^^ > + ip_options_len(asoc->base.sk); Please add a var for sctp_sk(asoc->base.sk)->pf->af. That should also help to not break the dereferencing into multiple lines. > + > max_data = SCTP_TRUNC4(max_data); > > /* If the the peer requested that we authenticate DATA chunks > @@ -210,7 +212,6 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc, > > /* Set first_len and then account for possible bundles on first frag */ > first_len = max_data; > - > /* Check to see if we have a pending SACK and try to let it be bundled > * with this message. Do this if we don't have any data queued already. > * To check that, look at out_qlen and retransmit list. > diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c > index a4b6ffb..49c9011 100644 > --- a/net/sctp/ipv6.c > +++ b/net/sctp/ipv6.c > @@ -423,6 +423,33 @@ static void sctp_v6_copy_addrlist(struct list_head *addrlist, > rcu_read_unlock(); > } > > +/* Copy over any ip options */ > +static void sctp_v6_copy_ip_options(struct sock *sk, struct sock *newsk) > +{ > + struct ipv6_pinfo *newnp, *np = inet6_sk(sk); > + struct ipv6_txoptions *opt; > + > + newnp = inet6_sk(newsk); > + > + rcu_read_lock(); > + opt = rcu_dereference(np->opt); > + if (opt) > + opt = ipv6_dup_options(newsk, opt); > + RCU_INIT_POINTER(newnp->opt, opt); > + rcu_read_unlock(); > +} > + > +/* Account for the IP options */ > +static int sctp_v6_ip_options_len(struct sock *sk) > +{ > + struct ipv6_pinfo *inet6 = inet6_sk(sk); > + > + if (inet6->opt) > + return inet6->opt->opt_flen + inet6->opt->opt_nflen; Seems we need RCU protection here. And please add a var for inet6->opt. > + else > + return 0; > +} > + > /* Initialize a sockaddr_storage from in incoming skb. */ > static void sctp_v6_from_skb(union sctp_addr *addr, struct sk_buff *skb, > int is_saddr) > @@ -662,7 +689,6 @@ static struct sock *sctp_v6_create_accept_sk(struct sock *sk, > struct sock *newsk; > struct ipv6_pinfo *newnp, *np = inet6_sk(sk); > struct sctp6_sock *newsctp6sk; > - struct ipv6_txoptions *opt; > > newsk = sk_alloc(sock_net(sk), PF_INET6, GFP_KERNEL, sk->sk_prot, kern); > if (!newsk) > @@ -685,12 +711,7 @@ static struct sock *sctp_v6_create_accept_sk(struct sock *sk, > newnp->ipv6_ac_list = NULL; > newnp->ipv6_fl_list = NULL; > > - rcu_read_lock(); > - opt = rcu_dereference(np->opt); > - if (opt) > - opt = ipv6_dup_options(newsk, opt); > - RCU_INIT_POINTER(newnp->opt, opt); > - rcu_read_unlock(); > + sctp_v6_copy_ip_options(sk, newsk); > > /* Initialize sk's sport, dport, rcv_saddr and daddr for getsockname() > * and getpeername(). > @@ -1033,6 +1054,7 @@ static struct sctp_af sctp_af_inet6 = { > .ecn_capable = sctp_v6_ecn_capable, > .net_header_len = sizeof(struct ipv6hdr), > .sockaddr_len = sizeof(struct sockaddr_in6), > + .ip_options_len = sctp_v6_ip_options_len, > #ifdef CONFIG_COMPAT > .compat_setsockopt = compat_ipv6_setsockopt, > .compat_getsockopt = compat_ipv6_getsockopt, > @@ -1051,6 +1073,7 @@ static struct sctp_pf sctp_pf_inet6 = { > .addr_to_user = sctp_v6_addr_to_user, > .to_sk_saddr = sctp_v6_to_sk_saddr, > .to_sk_daddr = sctp_v6_to_sk_daddr, > + .copy_ip_options = sctp_v6_copy_ip_options, > .af = &sctp_af_inet6, > }; > > diff --git a/net/sctp/output.c b/net/sctp/output.c > index 9d85049..85bcd5b 100644 > --- a/net/sctp/output.c > +++ b/net/sctp/output.c > @@ -151,7 +151,8 @@ void sctp_packet_init(struct sctp_packet *packet, > INIT_LIST_HEAD(&packet->chunk_list); > if (asoc) { > struct sctp_sock *sp = sctp_sk(asoc->base.sk); > - overhead = sp->pf->af->net_header_len; > + overhead = sp->pf->af->net_header_len + > + sp->pf->af->ip_options_len(asoc->base.sk); Here you may also add a var for sp->pf->af. > } else { > overhead = sizeof(struct ipv6hdr); > } > diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c > index 989a900..a9e54ac 100644 > --- a/net/sctp/protocol.c > +++ b/net/sctp/protocol.c > @@ -237,6 +237,38 @@ int sctp_copy_local_addr_list(struct net *net, struct sctp_bind_addr *bp, > return error; > } > > +/* Copy over any ip options */ > +static void sctp_v4_copy_ip_options(struct sock *sk, struct sock *newsk) > +{ > + struct inet_sock *newinet, *inet = inet_sk(sk); > + struct ip_options_rcu *inet_opt, *newopt = NULL; > + > + newinet = inet_sk(newsk); > + > + rcu_read_lock(); > + inet_opt = rcu_dereference(inet->inet_opt); > + if (inet_opt) { > + newopt = sock_kmalloc(newsk, sizeof(*inet_opt) + > + inet_opt->opt.optlen, GFP_ATOMIC); > + if (newopt) > + memcpy(newopt, inet_opt, sizeof(*inet_opt) + > + inet_opt->opt.optlen); > + } > + RCU_INIT_POINTER(newinet->inet_opt, newopt); > + rcu_read_unlock(); > +} > + > +/* Account for the IP options */ > +static int sctp_v4_ip_options_len(struct sock *sk) > +{ > + struct inet_sock *inet = inet_sk(sk); > + > + if (inet->inet_opt) > + return inet->inet_opt->opt.optlen; > + else > + return 0; > +} > + > /* 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) > @@ -590,6 +622,8 @@ static struct sock *sctp_v4_create_accept_sk(struct sock *sk, > sctp_copy_sock(newsk, sk, asoc); > sock_reset_flag(newsk, SOCK_ZAPPED); > > + sctp_v4_copy_ip_options(sk, newsk); > + > newinet = inet_sk(newsk); > > newinet->inet_daddr = asoc->peer.primary_addr.v4.sin_addr.s_addr; > @@ -1008,6 +1042,7 @@ static struct sctp_pf sctp_pf_inet = { > .addr_to_user = sctp_v4_addr_to_user, > .to_sk_saddr = sctp_v4_to_sk_saddr, > .to_sk_daddr = sctp_v4_to_sk_daddr, > + .copy_ip_options = sctp_v4_copy_ip_options, > .af = &sctp_af_inet > }; > > @@ -1092,6 +1127,7 @@ static struct sctp_af sctp_af_inet = { > .ecn_capable = sctp_v4_ecn_capable, > .net_header_len = sizeof(struct iphdr), > .sockaddr_len = sizeof(struct sockaddr_in), > + .ip_options_len = sctp_v4_ip_options_len, > #ifdef CONFIG_COMPAT > .compat_setsockopt = compat_ip_setsockopt, > .compat_getsockopt = compat_ip_getsockopt, > diff --git a/net/sctp/socket.c b/net/sctp/socket.c > index 8d76086..70355a0 100644 > --- a/net/sctp/socket.c > +++ b/net/sctp/socket.c > @@ -3124,6 +3124,7 @@ static int sctp_setsockopt_maxseg(struct sock *sk, char __user *optval, unsigned > if (asoc) { > if (val == 0) { > val = asoc->pathmtu; > + val -= sp->pf->af->ip_options_len(asoc->base.sk); > val -= sp->pf->af->net_header_len; Also here. May even be inside the if() block. Thanks > val -= sizeof(struct sctphdr) + > sizeof(struct sctp_data_chunk); > @@ -4917,9 +4918,11 @@ int sctp_do_peeloff(struct sock *sk, sctp_assoc_t id, struct socket **sockp) > sctp_copy_sock(sock->sk, sk, asoc); > > /* Make peeled-off sockets more like 1-1 accepted sockets. > - * Set the daddr and initialize id to something more random > + * Set the daddr and initialize id to something more random and also > + * copy over any ip options. > */ > sp->pf->to_sk_daddr(&asoc->peer.primary_addr, sk); > + sp->pf->copy_ip_options(sk, sock->sk); > > /* Populate the fields of the newsk from the oldsk and migrate the > * asoc to the newsk. > -- > 2.13.6 > > -- > To unsubscribe from this list: send the line "unsubscribe linux-sctp" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html >
On Tue, 2017-10-31 at 15:06 -0200, Marcelo Ricardo Leitner wrote: > Hello, > > On Tue, Oct 17, 2017 at 02:58:06PM +0100, Richard Haines wrote: > > Add ip option support to allow LSM security modules to utilise > > CIPSO/IPv4 > > and CALIPSO/IPv6 services. > > > > Signed-off-by: Richard Haines <richard_c_haines@btinternet.com> > > --- > > include/net/sctp/structs.h | 2 ++ > > net/sctp/chunk.c | 7 ++++--- > > net/sctp/ipv6.c | 37 ++++++++++++++++++++++++++++++-- > > ----- > > net/sctp/output.c | 3 ++- > > net/sctp/protocol.c | 36 > > ++++++++++++++++++++++++++++++++++++ > > net/sctp/socket.c | 5 ++++- > > 6 files changed, 78 insertions(+), 12 deletions(-) > > > > diff --git a/include/net/sctp/structs.h > > b/include/net/sctp/structs.h > > index 5ab29af..7767577 100644 > > --- a/include/net/sctp/structs.h > > +++ b/include/net/sctp/structs.h > > @@ -461,6 +461,7 @@ struct sctp_af { > > void (*ecn_capable)(struct sock *sk); > > __u16 net_header_len; > > int sockaddr_len; > > + int (*ip_options_len)(struct sock *sk); > > sa_family_t sa_family; > > struct list_head list; > > }; > > @@ -485,6 +486,7 @@ struct sctp_pf { > > int (*addr_to_user)(struct sctp_sock *sk, union sctp_addr > > *addr); > > void (*to_sk_saddr)(union sctp_addr *, struct sock *sk); > > void (*to_sk_daddr)(union sctp_addr *, struct sock *sk); > > + void (*copy_ip_options)(struct sock *sk, struct sock > > *newsk); > > struct sctp_af *af; > > }; > > > > diff --git a/net/sctp/chunk.c b/net/sctp/chunk.c > > index 1323d41..e49e240 100644 > > --- a/net/sctp/chunk.c > > +++ b/net/sctp/chunk.c > > @@ -153,7 +153,6 @@ static void sctp_datamsg_assign(struct > > sctp_datamsg *msg, struct sctp_chunk *chu > > chunk->msg = msg; > > } > > > > - > > /* A data chunk can have a maximum payload of (2^16 - 20). Break > > * down any such message into smaller chunks. Opportunistically, > > fragment > > * the chunks down to the current MTU constraints. We may get > > refragmented > > @@ -190,7 +189,10 @@ struct sctp_datamsg > > *sctp_datamsg_from_user(struct sctp_association *asoc, > > */ > > max_data = asoc->pathmtu - > > sctp_sk(asoc->base.sk)->pf->af->net_header_len > > - > > ^^^^^^^^ > > - sizeof(struct sctphdr) - sizeof(struct > > sctp_data_chunk); > > + sizeof(struct sctphdr) - sizeof(struct > > sctp_data_chunk) - > > + sctp_sk(asoc->base.sk)->pf->af-> > > ^^^^^^^^ > > + ip_options_len(asoc->base.sk); > > Please add a var for sctp_sk(asoc->base.sk)->pf->af. That should also > help to not break the dereferencing into multiple lines. DONE > > > + > > max_data = SCTP_TRUNC4(max_data); > > > > /* If the the peer requested that we authenticate DATA > > chunks > > @@ -210,7 +212,6 @@ struct sctp_datamsg > > *sctp_datamsg_from_user(struct sctp_association *asoc, > > > > /* Set first_len and then account for possible bundles on > > first frag */ > > first_len = max_data; > > - > > /* Check to see if we have a pending SACK and try to let > > it be bundled > > * with this message. Do this if we don't have any data > > queued already. > > * To check that, look at out_qlen and retransmit list. > > diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c > > index a4b6ffb..49c9011 100644 > > --- a/net/sctp/ipv6.c > > +++ b/net/sctp/ipv6.c > > @@ -423,6 +423,33 @@ static void sctp_v6_copy_addrlist(struct > > list_head *addrlist, > > rcu_read_unlock(); > > } > > > > +/* Copy over any ip options */ > > +static void sctp_v6_copy_ip_options(struct sock *sk, struct sock > > *newsk) > > +{ > > + struct ipv6_pinfo *newnp, *np = inet6_sk(sk); > > + struct ipv6_txoptions *opt; > > + > > + newnp = inet6_sk(newsk); > > + > > + rcu_read_lock(); > > + opt = rcu_dereference(np->opt); > > + if (opt) > > + opt = ipv6_dup_options(newsk, opt); > > + RCU_INIT_POINTER(newnp->opt, opt); > > + rcu_read_unlock(); > > +} > > + > > +/* Account for the IP options */ > > +static int sctp_v6_ip_options_len(struct sock *sk) > > +{ > > + struct ipv6_pinfo *inet6 = inet6_sk(sk); > > + > > + if (inet6->opt) > > + return inet6->opt->opt_flen + inet6->opt- > > >opt_nflen; > > Seems we need RCU protection here. > And please add a var for inet6->opt. I've changed and tested the following: /* Account for the IP options */ static int sctp_v6_ip_options_len(struct sock *sk) { struct ipv6_pinfo *np = inet6_sk(sk); struct ipv6_txoptions *opt; int len = 0; rcu_read_lock(); opt = rcu_dereference(np->opt); if (opt) len = opt->opt_flen + opt->opt_nflen; rcu_read_unlock(); return len; } > > > + else > > + return 0; > > +} > > + > > /* Initialize a sockaddr_storage from in incoming skb. */ > > static void sctp_v6_from_skb(union sctp_addr *addr, struct sk_buff > > *skb, > > int is_saddr) > > @@ -662,7 +689,6 @@ static struct sock > > *sctp_v6_create_accept_sk(struct sock *sk, > > struct sock *newsk; > > struct ipv6_pinfo *newnp, *np = inet6_sk(sk); > > struct sctp6_sock *newsctp6sk; > > - struct ipv6_txoptions *opt; > > > > newsk = sk_alloc(sock_net(sk), PF_INET6, GFP_KERNEL, sk- > > >sk_prot, kern); > > if (!newsk) > > @@ -685,12 +711,7 @@ static struct sock > > *sctp_v6_create_accept_sk(struct sock *sk, > > newnp->ipv6_ac_list = NULL; > > newnp->ipv6_fl_list = NULL; > > > > - rcu_read_lock(); > > - opt = rcu_dereference(np->opt); > > - if (opt) > > - opt = ipv6_dup_options(newsk, opt); > > - RCU_INIT_POINTER(newnp->opt, opt); > > - rcu_read_unlock(); > > + sctp_v6_copy_ip_options(sk, newsk); > > > > /* Initialize sk's sport, dport, rcv_saddr and daddr for > > getsockname() > > * and getpeername(). > > @@ -1033,6 +1054,7 @@ static struct sctp_af sctp_af_inet6 = { > > .ecn_capable = sctp_v6_ecn_capable, > > .net_header_len = sizeof(struct ipv6hdr), > > .sockaddr_len = sizeof(struct sockaddr_in6), > > + .ip_options_len = sctp_v6_ip_options_len, > > #ifdef CONFIG_COMPAT > > .compat_setsockopt = compat_ipv6_setsockopt, > > .compat_getsockopt = compat_ipv6_getsockopt, > > @@ -1051,6 +1073,7 @@ static struct sctp_pf sctp_pf_inet6 = { > > .addr_to_user = sctp_v6_addr_to_user, > > .to_sk_saddr = sctp_v6_to_sk_saddr, > > .to_sk_daddr = sctp_v6_to_sk_daddr, > > + .copy_ip_options = sctp_v6_copy_ip_options, > > .af = &sctp_af_inet6, > > }; > > > > diff --git a/net/sctp/output.c b/net/sctp/output.c > > index 9d85049..85bcd5b 100644 > > --- a/net/sctp/output.c > > +++ b/net/sctp/output.c > > @@ -151,7 +151,8 @@ void sctp_packet_init(struct sctp_packet > > *packet, > > INIT_LIST_HEAD(&packet->chunk_list); > > if (asoc) { > > struct sctp_sock *sp = sctp_sk(asoc->base.sk); > > - overhead = sp->pf->af->net_header_len; > > + overhead = sp->pf->af->net_header_len + > > + sp->pf->af->ip_options_len(asoc- > > >base.sk); > > Here you may also add a var for sp->pf->af. DONE > > > } else { > > overhead = sizeof(struct ipv6hdr); > > } > > diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c > > index 989a900..a9e54ac 100644 > > --- a/net/sctp/protocol.c > > +++ b/net/sctp/protocol.c > > @@ -237,6 +237,38 @@ int sctp_copy_local_addr_list(struct net *net, > > struct sctp_bind_addr *bp, > > return error; > > } > > > > +/* Copy over any ip options */ > > +static void sctp_v4_copy_ip_options(struct sock *sk, struct sock > > *newsk) > > +{ > > + struct inet_sock *newinet, *inet = inet_sk(sk); > > + struct ip_options_rcu *inet_opt, *newopt = NULL; > > + > > + newinet = inet_sk(newsk); > > + > > + rcu_read_lock(); > > + inet_opt = rcu_dereference(inet->inet_opt); > > + if (inet_opt) { > > + newopt = sock_kmalloc(newsk, sizeof(*inet_opt) + > > + inet_opt->opt.optlen, > > GFP_ATOMIC); > > + if (newopt) > > + memcpy(newopt, inet_opt, sizeof(*inet_opt) > > + > > + inet_opt->opt.optlen); > > + } > > + RCU_INIT_POINTER(newinet->inet_opt, newopt); > > + rcu_read_unlock(); > > +} > > + > > +/* Account for the IP options */ > > +static int sctp_v4_ip_options_len(struct sock *sk) > > +{ > > + struct inet_sock *inet = inet_sk(sk); > > + > > + if (inet->inet_opt) > > + return inet->inet_opt->opt.optlen; > > + else > > + return 0; > > +} > > + > > /* 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) > > @@ -590,6 +622,8 @@ static struct sock > > *sctp_v4_create_accept_sk(struct sock *sk, > > sctp_copy_sock(newsk, sk, asoc); > > sock_reset_flag(newsk, SOCK_ZAPPED); > > > > + sctp_v4_copy_ip_options(sk, newsk); > > + > > newinet = inet_sk(newsk); > > > > newinet->inet_daddr = asoc- > > >peer.primary_addr.v4.sin_addr.s_addr; > > @@ -1008,6 +1042,7 @@ static struct sctp_pf sctp_pf_inet = { > > .addr_to_user = sctp_v4_addr_to_user, > > .to_sk_saddr = sctp_v4_to_sk_saddr, > > .to_sk_daddr = sctp_v4_to_sk_daddr, > > + .copy_ip_options = sctp_v4_copy_ip_options, > > .af = &sctp_af_inet > > }; > > > > @@ -1092,6 +1127,7 @@ static struct sctp_af sctp_af_inet = { > > .ecn_capable = sctp_v4_ecn_capable, > > .net_header_len = sizeof(struct iphdr), > > .sockaddr_len = sizeof(struct sockaddr_in), > > + .ip_options_len = sctp_v4_ip_options_len, > > #ifdef CONFIG_COMPAT > > .compat_setsockopt = compat_ip_setsockopt, > > .compat_getsockopt = compat_ip_getsockopt, > > diff --git a/net/sctp/socket.c b/net/sctp/socket.c > > index 8d76086..70355a0 100644 > > --- a/net/sctp/socket.c > > +++ b/net/sctp/socket.c > > @@ -3124,6 +3124,7 @@ static int sctp_setsockopt_maxseg(struct sock > > *sk, char __user *optval, unsigned > > if (asoc) { > > if (val == 0) { > > val = asoc->pathmtu; > > + val -= sp->pf->af->ip_options_len(asoc- > > >base.sk); > > val -= sp->pf->af->net_header_len; > > Also here. May even be inside the if() block. DONE > > Thanks > > > val -= sizeof(struct sctphdr) + > > sizeof(struct > > sctp_data_chunk); > > @@ -4917,9 +4918,11 @@ int sctp_do_peeloff(struct sock *sk, > > sctp_assoc_t id, struct socket **sockp) > > sctp_copy_sock(sock->sk, sk, asoc); > > > > /* Make peeled-off sockets more like 1-1 accepted sockets. > > - * Set the daddr and initialize id to something more > > random > > + * Set the daddr and initialize id to something more > > random and also > > + * copy over any ip options. > > */ > > sp->pf->to_sk_daddr(&asoc->peer.primary_addr, sk); > > + sp->pf->copy_ip_options(sk, sock->sk); > > > > /* Populate the fields of the newsk from the oldsk and > > migrate the > > * asoc to the newsk. > > -- > > 2.13.6 > > > > -- > > To unsubscribe from this list: send the line "unsubscribe linux- > > sctp" in > > the body of a message to majordomo@vger.kernel.org > > More majordomo info at http://vger.kernel.org/majordomo-info.html > > > > -- > To unsubscribe from this list: send the line "unsubscribe linux- > security-module" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html
diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index 5ab29af..7767577 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -461,6 +461,7 @@ struct sctp_af { void (*ecn_capable)(struct sock *sk); __u16 net_header_len; int sockaddr_len; + int (*ip_options_len)(struct sock *sk); sa_family_t sa_family; struct list_head list; }; @@ -485,6 +486,7 @@ struct sctp_pf { int (*addr_to_user)(struct sctp_sock *sk, union sctp_addr *addr); void (*to_sk_saddr)(union sctp_addr *, struct sock *sk); void (*to_sk_daddr)(union sctp_addr *, struct sock *sk); + void (*copy_ip_options)(struct sock *sk, struct sock *newsk); struct sctp_af *af; }; diff --git a/net/sctp/chunk.c b/net/sctp/chunk.c index 1323d41..e49e240 100644 --- a/net/sctp/chunk.c +++ b/net/sctp/chunk.c @@ -153,7 +153,6 @@ static void sctp_datamsg_assign(struct sctp_datamsg *msg, struct sctp_chunk *chu chunk->msg = msg; } - /* A data chunk can have a maximum payload of (2^16 - 20). Break * down any such message into smaller chunks. Opportunistically, fragment * the chunks down to the current MTU constraints. We may get refragmented @@ -190,7 +189,10 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc, */ max_data = asoc->pathmtu - sctp_sk(asoc->base.sk)->pf->af->net_header_len - - sizeof(struct sctphdr) - sizeof(struct sctp_data_chunk); + sizeof(struct sctphdr) - sizeof(struct sctp_data_chunk) - + sctp_sk(asoc->base.sk)->pf->af-> + ip_options_len(asoc->base.sk); + max_data = SCTP_TRUNC4(max_data); /* If the the peer requested that we authenticate DATA chunks @@ -210,7 +212,6 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc, /* Set first_len and then account for possible bundles on first frag */ first_len = max_data; - /* Check to see if we have a pending SACK and try to let it be bundled * with this message. Do this if we don't have any data queued already. * To check that, look at out_qlen and retransmit list. diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c index a4b6ffb..49c9011 100644 --- a/net/sctp/ipv6.c +++ b/net/sctp/ipv6.c @@ -423,6 +423,33 @@ static void sctp_v6_copy_addrlist(struct list_head *addrlist, rcu_read_unlock(); } +/* Copy over any ip options */ +static void sctp_v6_copy_ip_options(struct sock *sk, struct sock *newsk) +{ + struct ipv6_pinfo *newnp, *np = inet6_sk(sk); + struct ipv6_txoptions *opt; + + newnp = inet6_sk(newsk); + + rcu_read_lock(); + opt = rcu_dereference(np->opt); + if (opt) + opt = ipv6_dup_options(newsk, opt); + RCU_INIT_POINTER(newnp->opt, opt); + rcu_read_unlock(); +} + +/* Account for the IP options */ +static int sctp_v6_ip_options_len(struct sock *sk) +{ + struct ipv6_pinfo *inet6 = inet6_sk(sk); + + if (inet6->opt) + return inet6->opt->opt_flen + inet6->opt->opt_nflen; + else + return 0; +} + /* Initialize a sockaddr_storage from in incoming skb. */ static void sctp_v6_from_skb(union sctp_addr *addr, struct sk_buff *skb, int is_saddr) @@ -662,7 +689,6 @@ static struct sock *sctp_v6_create_accept_sk(struct sock *sk, struct sock *newsk; struct ipv6_pinfo *newnp, *np = inet6_sk(sk); struct sctp6_sock *newsctp6sk; - struct ipv6_txoptions *opt; newsk = sk_alloc(sock_net(sk), PF_INET6, GFP_KERNEL, sk->sk_prot, kern); if (!newsk) @@ -685,12 +711,7 @@ static struct sock *sctp_v6_create_accept_sk(struct sock *sk, newnp->ipv6_ac_list = NULL; newnp->ipv6_fl_list = NULL; - rcu_read_lock(); - opt = rcu_dereference(np->opt); - if (opt) - opt = ipv6_dup_options(newsk, opt); - RCU_INIT_POINTER(newnp->opt, opt); - rcu_read_unlock(); + sctp_v6_copy_ip_options(sk, newsk); /* Initialize sk's sport, dport, rcv_saddr and daddr for getsockname() * and getpeername(). @@ -1033,6 +1054,7 @@ static struct sctp_af sctp_af_inet6 = { .ecn_capable = sctp_v6_ecn_capable, .net_header_len = sizeof(struct ipv6hdr), .sockaddr_len = sizeof(struct sockaddr_in6), + .ip_options_len = sctp_v6_ip_options_len, #ifdef CONFIG_COMPAT .compat_setsockopt = compat_ipv6_setsockopt, .compat_getsockopt = compat_ipv6_getsockopt, @@ -1051,6 +1073,7 @@ static struct sctp_pf sctp_pf_inet6 = { .addr_to_user = sctp_v6_addr_to_user, .to_sk_saddr = sctp_v6_to_sk_saddr, .to_sk_daddr = sctp_v6_to_sk_daddr, + .copy_ip_options = sctp_v6_copy_ip_options, .af = &sctp_af_inet6, }; diff --git a/net/sctp/output.c b/net/sctp/output.c index 9d85049..85bcd5b 100644 --- a/net/sctp/output.c +++ b/net/sctp/output.c @@ -151,7 +151,8 @@ void sctp_packet_init(struct sctp_packet *packet, INIT_LIST_HEAD(&packet->chunk_list); if (asoc) { struct sctp_sock *sp = sctp_sk(asoc->base.sk); - overhead = sp->pf->af->net_header_len; + overhead = sp->pf->af->net_header_len + + sp->pf->af->ip_options_len(asoc->base.sk); } else { overhead = sizeof(struct ipv6hdr); } diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index 989a900..a9e54ac 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -237,6 +237,38 @@ int sctp_copy_local_addr_list(struct net *net, struct sctp_bind_addr *bp, return error; } +/* Copy over any ip options */ +static void sctp_v4_copy_ip_options(struct sock *sk, struct sock *newsk) +{ + struct inet_sock *newinet, *inet = inet_sk(sk); + struct ip_options_rcu *inet_opt, *newopt = NULL; + + newinet = inet_sk(newsk); + + rcu_read_lock(); + inet_opt = rcu_dereference(inet->inet_opt); + if (inet_opt) { + newopt = sock_kmalloc(newsk, sizeof(*inet_opt) + + inet_opt->opt.optlen, GFP_ATOMIC); + if (newopt) + memcpy(newopt, inet_opt, sizeof(*inet_opt) + + inet_opt->opt.optlen); + } + RCU_INIT_POINTER(newinet->inet_opt, newopt); + rcu_read_unlock(); +} + +/* Account for the IP options */ +static int sctp_v4_ip_options_len(struct sock *sk) +{ + struct inet_sock *inet = inet_sk(sk); + + if (inet->inet_opt) + return inet->inet_opt->opt.optlen; + else + return 0; +} + /* 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) @@ -590,6 +622,8 @@ static struct sock *sctp_v4_create_accept_sk(struct sock *sk, sctp_copy_sock(newsk, sk, asoc); sock_reset_flag(newsk, SOCK_ZAPPED); + sctp_v4_copy_ip_options(sk, newsk); + newinet = inet_sk(newsk); newinet->inet_daddr = asoc->peer.primary_addr.v4.sin_addr.s_addr; @@ -1008,6 +1042,7 @@ static struct sctp_pf sctp_pf_inet = { .addr_to_user = sctp_v4_addr_to_user, .to_sk_saddr = sctp_v4_to_sk_saddr, .to_sk_daddr = sctp_v4_to_sk_daddr, + .copy_ip_options = sctp_v4_copy_ip_options, .af = &sctp_af_inet }; @@ -1092,6 +1127,7 @@ static struct sctp_af sctp_af_inet = { .ecn_capable = sctp_v4_ecn_capable, .net_header_len = sizeof(struct iphdr), .sockaddr_len = sizeof(struct sockaddr_in), + .ip_options_len = sctp_v4_ip_options_len, #ifdef CONFIG_COMPAT .compat_setsockopt = compat_ip_setsockopt, .compat_getsockopt = compat_ip_getsockopt, diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 8d76086..70355a0 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -3124,6 +3124,7 @@ static int sctp_setsockopt_maxseg(struct sock *sk, char __user *optval, unsigned if (asoc) { if (val == 0) { val = asoc->pathmtu; + val -= sp->pf->af->ip_options_len(asoc->base.sk); val -= sp->pf->af->net_header_len; val -= sizeof(struct sctphdr) + sizeof(struct sctp_data_chunk); @@ -4917,9 +4918,11 @@ int sctp_do_peeloff(struct sock *sk, sctp_assoc_t id, struct socket **sockp) sctp_copy_sock(sock->sk, sk, asoc); /* Make peeled-off sockets more like 1-1 accepted sockets. - * Set the daddr and initialize id to something more random + * Set the daddr and initialize id to something more random and also + * copy over any ip options. */ sp->pf->to_sk_daddr(&asoc->peer.primary_addr, sk); + sp->pf->copy_ip_options(sk, sock->sk); /* Populate the fields of the newsk from the oldsk and migrate the * asoc to the newsk.
Add ip option support to allow LSM security modules to utilise CIPSO/IPv4 and CALIPSO/IPv6 services. Signed-off-by: Richard Haines <richard_c_haines@btinternet.com> --- include/net/sctp/structs.h | 2 ++ net/sctp/chunk.c | 7 ++++--- net/sctp/ipv6.c | 37 ++++++++++++++++++++++++++++++------- net/sctp/output.c | 3 ++- net/sctp/protocol.c | 36 ++++++++++++++++++++++++++++++++++++ net/sctp/socket.c | 5 ++++- 6 files changed, 78 insertions(+), 12 deletions(-)