mirror of
https://github.com/torvalds/linux.git
synced 2024-11-26 06:02:05 +00:00
sctp: Add ip option support
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> Acked-by: Neil Horman <nhorman@tuxdriver.com> Acked-by: Marcelo Ricardo Leitner <marcelo.leitner@gmail.com> Signed-off-by: Paul Moore <paul@paul-moore.com>
This commit is contained in:
parent
72e89f5008
commit
b7e10c25b8
@ -441,9 +441,11 @@ static inline int sctp_list_single_entry(struct list_head *head)
|
||||
static inline int sctp_frag_point(const struct sctp_association *asoc, int pmtu)
|
||||
{
|
||||
struct sctp_sock *sp = sctp_sk(asoc->base.sk);
|
||||
struct sctp_af *af = sp->pf->af;
|
||||
int frag = pmtu;
|
||||
|
||||
frag -= sp->pf->af->net_header_len;
|
||||
frag -= af->ip_options_len(asoc->base.sk);
|
||||
frag -= af->net_header_len;
|
||||
frag -= sizeof(struct sctphdr) + sctp_datachk_len(&asoc->stream);
|
||||
|
||||
if (asoc->user_frag)
|
||||
|
@ -491,6 +491,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;
|
||||
};
|
||||
@ -515,6 +516,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;
|
||||
};
|
||||
|
||||
|
@ -171,6 +171,8 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc,
|
||||
struct list_head *pos, *temp;
|
||||
struct sctp_chunk *chunk;
|
||||
struct sctp_datamsg *msg;
|
||||
struct sctp_sock *sp;
|
||||
struct sctp_af *af;
|
||||
int err;
|
||||
|
||||
msg = sctp_datamsg_new(GFP_KERNEL);
|
||||
@ -189,9 +191,11 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc,
|
||||
/* This is the biggest possible DATA chunk that can fit into
|
||||
* the packet
|
||||
*/
|
||||
max_data = asoc->pathmtu -
|
||||
sctp_sk(asoc->base.sk)->pf->af->net_header_len -
|
||||
sizeof(struct sctphdr) - sctp_datachk_len(&asoc->stream);
|
||||
sp = sctp_sk(asoc->base.sk);
|
||||
af = sp->pf->af;
|
||||
max_data = asoc->pathmtu - af->net_header_len -
|
||||
sizeof(struct sctphdr) - sctp_datachk_len(&asoc->stream) -
|
||||
af->ip_options_len(asoc->base.sk);
|
||||
max_data = SCTP_TRUNC4(max_data);
|
||||
|
||||
/* If the the peer requested that we authenticate DATA chunks
|
||||
|
@ -427,6 +427,41 @@ 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);
|
||||
if (!opt)
|
||||
pr_err("%s: Failed to copy ip options\n", __func__);
|
||||
}
|
||||
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 *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;
|
||||
}
|
||||
|
||||
/* 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)
|
||||
@ -666,7 +701,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)
|
||||
@ -689,12 +723,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().
|
||||
@ -1041,6 +1070,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,
|
||||
@ -1059,6 +1089,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,
|
||||
};
|
||||
|
||||
|
@ -69,7 +69,11 @@ static enum sctp_xmit sctp_packet_will_fit(struct sctp_packet *packet,
|
||||
|
||||
static void sctp_packet_reset(struct sctp_packet *packet)
|
||||
{
|
||||
/* sctp_packet_transmit() relies on this to reset size to the
|
||||
* current overhead after sending packets.
|
||||
*/
|
||||
packet->size = packet->overhead;
|
||||
|
||||
packet->has_cookie_echo = 0;
|
||||
packet->has_sack = 0;
|
||||
packet->has_data = 0;
|
||||
@ -87,6 +91,7 @@ void sctp_packet_config(struct sctp_packet *packet, __u32 vtag,
|
||||
struct sctp_transport *tp = packet->transport;
|
||||
struct sctp_association *asoc = tp->asoc;
|
||||
struct sock *sk;
|
||||
size_t overhead = sizeof(struct ipv6hdr) + sizeof(struct sctphdr);
|
||||
|
||||
pr_debug("%s: packet:%p vtag:0x%x\n", __func__, packet, vtag);
|
||||
packet->vtag = vtag;
|
||||
@ -95,10 +100,22 @@ void sctp_packet_config(struct sctp_packet *packet, __u32 vtag,
|
||||
if (!sctp_packet_empty(packet))
|
||||
return;
|
||||
|
||||
/* set packet max_size with pathmtu */
|
||||
/* set packet max_size with pathmtu, then calculate overhead */
|
||||
packet->max_size = tp->pathmtu;
|
||||
if (!asoc)
|
||||
if (asoc) {
|
||||
struct sctp_sock *sp = sctp_sk(asoc->base.sk);
|
||||
struct sctp_af *af = sp->pf->af;
|
||||
|
||||
overhead = af->net_header_len +
|
||||
af->ip_options_len(asoc->base.sk);
|
||||
overhead += sizeof(struct sctphdr);
|
||||
packet->overhead = overhead;
|
||||
packet->size = overhead;
|
||||
} else {
|
||||
packet->overhead = overhead;
|
||||
packet->size = overhead;
|
||||
return;
|
||||
}
|
||||
|
||||
/* update dst or transport pathmtu if in need */
|
||||
sk = asoc->base.sk;
|
||||
@ -140,23 +157,14 @@ void sctp_packet_init(struct sctp_packet *packet,
|
||||
struct sctp_transport *transport,
|
||||
__u16 sport, __u16 dport)
|
||||
{
|
||||
struct sctp_association *asoc = transport->asoc;
|
||||
size_t overhead;
|
||||
|
||||
pr_debug("%s: packet:%p transport:%p\n", __func__, packet, transport);
|
||||
|
||||
packet->transport = transport;
|
||||
packet->source_port = sport;
|
||||
packet->destination_port = dport;
|
||||
INIT_LIST_HEAD(&packet->chunk_list);
|
||||
if (asoc) {
|
||||
struct sctp_sock *sp = sctp_sk(asoc->base.sk);
|
||||
overhead = sp->pf->af->net_header_len;
|
||||
} else {
|
||||
overhead = sizeof(struct ipv6hdr);
|
||||
}
|
||||
overhead += sizeof(struct sctphdr);
|
||||
packet->overhead = overhead;
|
||||
/* The overhead will be calculated by sctp_packet_config() */
|
||||
packet->overhead = 0;
|
||||
sctp_packet_reset(packet);
|
||||
packet->vtag = 0;
|
||||
}
|
||||
|
@ -237,6 +237,45 @@ 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);
|
||||
else
|
||||
pr_err("%s: Failed to copy ip options\n", __func__);
|
||||
}
|
||||
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);
|
||||
struct ip_options_rcu *inet_opt;
|
||||
int len = 0;
|
||||
|
||||
rcu_read_lock();
|
||||
inet_opt = rcu_dereference(inet->inet_opt);
|
||||
if (inet_opt)
|
||||
len = inet_opt->opt.optlen;
|
||||
|
||||
rcu_read_unlock();
|
||||
return len;
|
||||
}
|
||||
|
||||
/* 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)
|
||||
@ -588,6 +627,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;
|
||||
@ -1006,6 +1047,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
|
||||
};
|
||||
|
||||
@ -1090,6 +1132,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,
|
||||
|
@ -3138,6 +3138,7 @@ static int sctp_setsockopt_mappedv4(struct sock *sk, char __user *optval, unsign
|
||||
static int sctp_setsockopt_maxseg(struct sock *sk, char __user *optval, unsigned int optlen)
|
||||
{
|
||||
struct sctp_sock *sp = sctp_sk(sk);
|
||||
struct sctp_af *af = sp->pf->af;
|
||||
struct sctp_assoc_value params;
|
||||
struct sctp_association *asoc;
|
||||
int val;
|
||||
@ -3162,7 +3163,8 @@ static int sctp_setsockopt_maxseg(struct sock *sk, char __user *optval, unsigned
|
||||
if (val) {
|
||||
int min_len, max_len;
|
||||
|
||||
min_len = SCTP_DEFAULT_MINSEGMENT - sp->pf->af->net_header_len;
|
||||
min_len = SCTP_DEFAULT_MINSEGMENT - af->net_header_len;
|
||||
min_len -= af->ip_options_len(sk);
|
||||
min_len -= sizeof(struct sctphdr) +
|
||||
sizeof(struct sctp_data_chunk);
|
||||
|
||||
@ -3175,7 +3177,8 @@ static int sctp_setsockopt_maxseg(struct sock *sk, char __user *optval, unsigned
|
||||
asoc = sctp_id2assoc(sk, params.assoc_id);
|
||||
if (asoc) {
|
||||
if (val == 0) {
|
||||
val = asoc->pathmtu - sp->pf->af->net_header_len;
|
||||
val = asoc->pathmtu - af->net_header_len;
|
||||
val -= af->ip_options_len(sk);
|
||||
val -= sizeof(struct sctphdr) +
|
||||
sctp_datachk_len(&asoc->stream);
|
||||
}
|
||||
@ -5087,9 +5090,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.
|
||||
|
Loading…
Reference in New Issue
Block a user