Skip to content

Commit b7e10c2

Browse files
Richard Hainespcmoore
authored andcommitted
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>
1 parent 72e89f5 commit b7e10c2

File tree

7 files changed

+122
-27
lines changed

7 files changed

+122
-27
lines changed

include/net/sctp/sctp.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -441,9 +441,11 @@ static inline int sctp_list_single_entry(struct list_head *head)
441441
static inline int sctp_frag_point(const struct sctp_association *asoc, int pmtu)
442442
{
443443
struct sctp_sock *sp = sctp_sk(asoc->base.sk);
444+
struct sctp_af *af = sp->pf->af;
444445
int frag = pmtu;
445446

446-
frag -= sp->pf->af->net_header_len;
447+
frag -= af->ip_options_len(asoc->base.sk);
448+
frag -= af->net_header_len;
447449
frag -= sizeof(struct sctphdr) + sctp_datachk_len(&asoc->stream);
448450

449451
if (asoc->user_frag)

include/net/sctp/structs.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,7 @@ struct sctp_af {
491491
void (*ecn_capable)(struct sock *sk);
492492
__u16 net_header_len;
493493
int sockaddr_len;
494+
int (*ip_options_len)(struct sock *sk);
494495
sa_family_t sa_family;
495496
struct list_head list;
496497
};
@@ -515,6 +516,7 @@ struct sctp_pf {
515516
int (*addr_to_user)(struct sctp_sock *sk, union sctp_addr *addr);
516517
void (*to_sk_saddr)(union sctp_addr *, struct sock *sk);
517518
void (*to_sk_daddr)(union sctp_addr *, struct sock *sk);
519+
void (*copy_ip_options)(struct sock *sk, struct sock *newsk);
518520
struct sctp_af *af;
519521
};
520522

net/sctp/chunk.c

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,8 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc,
171171
struct list_head *pos, *temp;
172172
struct sctp_chunk *chunk;
173173
struct sctp_datamsg *msg;
174+
struct sctp_sock *sp;
175+
struct sctp_af *af;
174176
int err;
175177

176178
msg = sctp_datamsg_new(GFP_KERNEL);
@@ -189,9 +191,11 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc,
189191
/* This is the biggest possible DATA chunk that can fit into
190192
* the packet
191193
*/
192-
max_data = asoc->pathmtu -
193-
sctp_sk(asoc->base.sk)->pf->af->net_header_len -
194-
sizeof(struct sctphdr) - sctp_datachk_len(&asoc->stream);
194+
sp = sctp_sk(asoc->base.sk);
195+
af = sp->pf->af;
196+
max_data = asoc->pathmtu - af->net_header_len -
197+
sizeof(struct sctphdr) - sctp_datachk_len(&asoc->stream) -
198+
af->ip_options_len(asoc->base.sk);
195199
max_data = SCTP_TRUNC4(max_data);
196200

197201
/* If the the peer requested that we authenticate DATA chunks

net/sctp/ipv6.c

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,41 @@ static void sctp_v6_copy_addrlist(struct list_head *addrlist,
427427
rcu_read_unlock();
428428
}
429429

430+
/* Copy over any ip options */
431+
static void sctp_v6_copy_ip_options(struct sock *sk, struct sock *newsk)
432+
{
433+
struct ipv6_pinfo *newnp, *np = inet6_sk(sk);
434+
struct ipv6_txoptions *opt;
435+
436+
newnp = inet6_sk(newsk);
437+
438+
rcu_read_lock();
439+
opt = rcu_dereference(np->opt);
440+
if (opt) {
441+
opt = ipv6_dup_options(newsk, opt);
442+
if (!opt)
443+
pr_err("%s: Failed to copy ip options\n", __func__);
444+
}
445+
RCU_INIT_POINTER(newnp->opt, opt);
446+
rcu_read_unlock();
447+
}
448+
449+
/* Account for the IP options */
450+
static int sctp_v6_ip_options_len(struct sock *sk)
451+
{
452+
struct ipv6_pinfo *np = inet6_sk(sk);
453+
struct ipv6_txoptions *opt;
454+
int len = 0;
455+
456+
rcu_read_lock();
457+
opt = rcu_dereference(np->opt);
458+
if (opt)
459+
len = opt->opt_flen + opt->opt_nflen;
460+
461+
rcu_read_unlock();
462+
return len;
463+
}
464+
430465
/* Initialize a sockaddr_storage from in incoming skb. */
431466
static void sctp_v6_from_skb(union sctp_addr *addr, struct sk_buff *skb,
432467
int is_saddr)
@@ -666,7 +701,6 @@ static struct sock *sctp_v6_create_accept_sk(struct sock *sk,
666701
struct sock *newsk;
667702
struct ipv6_pinfo *newnp, *np = inet6_sk(sk);
668703
struct sctp6_sock *newsctp6sk;
669-
struct ipv6_txoptions *opt;
670704

671705
newsk = sk_alloc(sock_net(sk), PF_INET6, GFP_KERNEL, sk->sk_prot, kern);
672706
if (!newsk)
@@ -689,12 +723,7 @@ static struct sock *sctp_v6_create_accept_sk(struct sock *sk,
689723
newnp->ipv6_ac_list = NULL;
690724
newnp->ipv6_fl_list = NULL;
691725

692-
rcu_read_lock();
693-
opt = rcu_dereference(np->opt);
694-
if (opt)
695-
opt = ipv6_dup_options(newsk, opt);
696-
RCU_INIT_POINTER(newnp->opt, opt);
697-
rcu_read_unlock();
726+
sctp_v6_copy_ip_options(sk, newsk);
698727

699728
/* Initialize sk's sport, dport, rcv_saddr and daddr for getsockname()
700729
* and getpeername().
@@ -1041,6 +1070,7 @@ static struct sctp_af sctp_af_inet6 = {
10411070
.ecn_capable = sctp_v6_ecn_capable,
10421071
.net_header_len = sizeof(struct ipv6hdr),
10431072
.sockaddr_len = sizeof(struct sockaddr_in6),
1073+
.ip_options_len = sctp_v6_ip_options_len,
10441074
#ifdef CONFIG_COMPAT
10451075
.compat_setsockopt = compat_ipv6_setsockopt,
10461076
.compat_getsockopt = compat_ipv6_getsockopt,
@@ -1059,6 +1089,7 @@ static struct sctp_pf sctp_pf_inet6 = {
10591089
.addr_to_user = sctp_v6_addr_to_user,
10601090
.to_sk_saddr = sctp_v6_to_sk_saddr,
10611091
.to_sk_daddr = sctp_v6_to_sk_daddr,
1092+
.copy_ip_options = sctp_v6_copy_ip_options,
10621093
.af = &sctp_af_inet6,
10631094
};
10641095

net/sctp/output.c

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,11 @@ static enum sctp_xmit sctp_packet_will_fit(struct sctp_packet *packet,
6969

7070
static void sctp_packet_reset(struct sctp_packet *packet)
7171
{
72+
/* sctp_packet_transmit() relies on this to reset size to the
73+
* current overhead after sending packets.
74+
*/
7275
packet->size = packet->overhead;
76+
7377
packet->has_cookie_echo = 0;
7478
packet->has_sack = 0;
7579
packet->has_data = 0;
@@ -87,6 +91,7 @@ void sctp_packet_config(struct sctp_packet *packet, __u32 vtag,
8791
struct sctp_transport *tp = packet->transport;
8892
struct sctp_association *asoc = tp->asoc;
8993
struct sock *sk;
94+
size_t overhead = sizeof(struct ipv6hdr) + sizeof(struct sctphdr);
9095

9196
pr_debug("%s: packet:%p vtag:0x%x\n", __func__, packet, vtag);
9297
packet->vtag = vtag;
@@ -95,10 +100,22 @@ void sctp_packet_config(struct sctp_packet *packet, __u32 vtag,
95100
if (!sctp_packet_empty(packet))
96101
return;
97102

98-
/* set packet max_size with pathmtu */
103+
/* set packet max_size with pathmtu, then calculate overhead */
99104
packet->max_size = tp->pathmtu;
100-
if (!asoc)
105+
if (asoc) {
106+
struct sctp_sock *sp = sctp_sk(asoc->base.sk);
107+
struct sctp_af *af = sp->pf->af;
108+
109+
overhead = af->net_header_len +
110+
af->ip_options_len(asoc->base.sk);
111+
overhead += sizeof(struct sctphdr);
112+
packet->overhead = overhead;
113+
packet->size = overhead;
114+
} else {
115+
packet->overhead = overhead;
116+
packet->size = overhead;
101117
return;
118+
}
102119

103120
/* update dst or transport pathmtu if in need */
104121
sk = asoc->base.sk;
@@ -140,23 +157,14 @@ void sctp_packet_init(struct sctp_packet *packet,
140157
struct sctp_transport *transport,
141158
__u16 sport, __u16 dport)
142159
{
143-
struct sctp_association *asoc = transport->asoc;
144-
size_t overhead;
145-
146160
pr_debug("%s: packet:%p transport:%p\n", __func__, packet, transport);
147161

148162
packet->transport = transport;
149163
packet->source_port = sport;
150164
packet->destination_port = dport;
151165
INIT_LIST_HEAD(&packet->chunk_list);
152-
if (asoc) {
153-
struct sctp_sock *sp = sctp_sk(asoc->base.sk);
154-
overhead = sp->pf->af->net_header_len;
155-
} else {
156-
overhead = sizeof(struct ipv6hdr);
157-
}
158-
overhead += sizeof(struct sctphdr);
159-
packet->overhead = overhead;
166+
/* The overhead will be calculated by sctp_packet_config() */
167+
packet->overhead = 0;
160168
sctp_packet_reset(packet);
161169
packet->vtag = 0;
162170
}

net/sctp/protocol.c

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,45 @@ int sctp_copy_local_addr_list(struct net *net, struct sctp_bind_addr *bp,
237237
return error;
238238
}
239239

240+
/* Copy over any ip options */
241+
static void sctp_v4_copy_ip_options(struct sock *sk, struct sock *newsk)
242+
{
243+
struct inet_sock *newinet, *inet = inet_sk(sk);
244+
struct ip_options_rcu *inet_opt, *newopt = NULL;
245+
246+
newinet = inet_sk(newsk);
247+
248+
rcu_read_lock();
249+
inet_opt = rcu_dereference(inet->inet_opt);
250+
if (inet_opt) {
251+
newopt = sock_kmalloc(newsk, sizeof(*inet_opt) +
252+
inet_opt->opt.optlen, GFP_ATOMIC);
253+
if (newopt)
254+
memcpy(newopt, inet_opt, sizeof(*inet_opt) +
255+
inet_opt->opt.optlen);
256+
else
257+
pr_err("%s: Failed to copy ip options\n", __func__);
258+
}
259+
RCU_INIT_POINTER(newinet->inet_opt, newopt);
260+
rcu_read_unlock();
261+
}
262+
263+
/* Account for the IP options */
264+
static int sctp_v4_ip_options_len(struct sock *sk)
265+
{
266+
struct inet_sock *inet = inet_sk(sk);
267+
struct ip_options_rcu *inet_opt;
268+
int len = 0;
269+
270+
rcu_read_lock();
271+
inet_opt = rcu_dereference(inet->inet_opt);
272+
if (inet_opt)
273+
len = inet_opt->opt.optlen;
274+
275+
rcu_read_unlock();
276+
return len;
277+
}
278+
240279
/* Initialize a sctp_addr from in incoming skb. */
241280
static void sctp_v4_from_skb(union sctp_addr *addr, struct sk_buff *skb,
242281
int is_saddr)
@@ -588,6 +627,8 @@ static struct sock *sctp_v4_create_accept_sk(struct sock *sk,
588627
sctp_copy_sock(newsk, sk, asoc);
589628
sock_reset_flag(newsk, SOCK_ZAPPED);
590629

630+
sctp_v4_copy_ip_options(sk, newsk);
631+
591632
newinet = inet_sk(newsk);
592633

593634
newinet->inet_daddr = asoc->peer.primary_addr.v4.sin_addr.s_addr;
@@ -1006,6 +1047,7 @@ static struct sctp_pf sctp_pf_inet = {
10061047
.addr_to_user = sctp_v4_addr_to_user,
10071048
.to_sk_saddr = sctp_v4_to_sk_saddr,
10081049
.to_sk_daddr = sctp_v4_to_sk_daddr,
1050+
.copy_ip_options = sctp_v4_copy_ip_options,
10091051
.af = &sctp_af_inet
10101052
};
10111053

@@ -1090,6 +1132,7 @@ static struct sctp_af sctp_af_inet = {
10901132
.ecn_capable = sctp_v4_ecn_capable,
10911133
.net_header_len = sizeof(struct iphdr),
10921134
.sockaddr_len = sizeof(struct sockaddr_in),
1135+
.ip_options_len = sctp_v4_ip_options_len,
10931136
#ifdef CONFIG_COMPAT
10941137
.compat_setsockopt = compat_ip_setsockopt,
10951138
.compat_getsockopt = compat_ip_getsockopt,

net/sctp/socket.c

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3138,6 +3138,7 @@ static int sctp_setsockopt_mappedv4(struct sock *sk, char __user *optval, unsign
31383138
static int sctp_setsockopt_maxseg(struct sock *sk, char __user *optval, unsigned int optlen)
31393139
{
31403140
struct sctp_sock *sp = sctp_sk(sk);
3141+
struct sctp_af *af = sp->pf->af;
31413142
struct sctp_assoc_value params;
31423143
struct sctp_association *asoc;
31433144
int val;
@@ -3162,7 +3163,8 @@ static int sctp_setsockopt_maxseg(struct sock *sk, char __user *optval, unsigned
31623163
if (val) {
31633164
int min_len, max_len;
31643165

3165-
min_len = SCTP_DEFAULT_MINSEGMENT - sp->pf->af->net_header_len;
3166+
min_len = SCTP_DEFAULT_MINSEGMENT - af->net_header_len;
3167+
min_len -= af->ip_options_len(sk);
31663168
min_len -= sizeof(struct sctphdr) +
31673169
sizeof(struct sctp_data_chunk);
31683170

@@ -3175,7 +3177,8 @@ static int sctp_setsockopt_maxseg(struct sock *sk, char __user *optval, unsigned
31753177
asoc = sctp_id2assoc(sk, params.assoc_id);
31763178
if (asoc) {
31773179
if (val == 0) {
3178-
val = asoc->pathmtu - sp->pf->af->net_header_len;
3180+
val = asoc->pathmtu - af->net_header_len;
3181+
val -= af->ip_options_len(sk);
31793182
val -= sizeof(struct sctphdr) +
31803183
sctp_datachk_len(&asoc->stream);
31813184
}
@@ -5087,9 +5090,11 @@ int sctp_do_peeloff(struct sock *sk, sctp_assoc_t id, struct socket **sockp)
50875090
sctp_copy_sock(sock->sk, sk, asoc);
50885091

50895092
/* Make peeled-off sockets more like 1-1 accepted sockets.
5090-
* Set the daddr and initialize id to something more random
5093+
* Set the daddr and initialize id to something more random and also
5094+
* copy over any ip options.
50915095
*/
50925096
sp->pf->to_sk_daddr(&asoc->peer.primary_addr, sk);
5097+
sp->pf->copy_ip_options(sk, sock->sk);
50935098

50945099
/* Populate the fields of the newsk from the oldsk and migrate the
50955100
* asoc to the newsk.

0 commit comments

Comments
 (0)