Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b7e10c25 authored by Richard Haines's avatar Richard Haines Committed by Paul Moore
Browse files

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: default avatarRichard Haines <richard_c_haines@btinternet.com>
Acked-by: default avatarNeil Horman <nhorman@tuxdriver.com>
Acked-by: default avatarMarcelo Ricardo Leitner <marcelo.leitner@gmail.com>
Signed-off-by: default avatarPaul Moore <paul@paul-moore.com>
parent 72e89f50
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -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)
+2 −0
Original line number Diff line number Diff line
@@ -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;
};

+7 −3
Original line number Diff line number Diff line
@@ -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
+38 −7
Original line number Diff line number Diff line
@@ -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,
};

+21 −13
Original line number Diff line number Diff line
@@ -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;
}
Loading