Skip to contentSkip to conversation

Shrink sk_buff and add a security blob

RFC PATCH New Paul Moore 1 participant 4 messages Apr 16, 2013

Subsystems (9)
lore
Share & tools
lore

Paul Moore

author
#0 lore raw

cover Shrink sk_buff and add a security blob

This patchset is a follow-up to the recent discussion around sk_buff size and the desire to add a LSM security blob to the sk_buff. The patches proposed here do indeed shrink the size of the sk_buff from 256 bytes down to 248 bytes on my x86_64 system while adding a LSM security blob, but there is a tradeoff in that the patches overload the skb_shared_info->destructor_arg field (more info/detail in the description of patch 3/3). The overloading only takes place for LSMs which make use of the blob, all other cases behave as they currently do. I wouldn't say this solution is perfect, but it does both shrink the sk_buff and add a security blob through what I believe to be the least invasive approach.

I'm posting this to restart the discussion around these changes now that we have a set of patches to discuss, critique, etc. Hopefully we can find some middle ground that gets everyone enough of what they want, if not all.

The standard disclaimers apply. This is a RFC patch; it builds, boots, and passes the basic unit tests I've thrown at it, but I can't say I've tested it throughly. This includes building the kernel with all the different, relevant CONFIG options. YMMV.

---

Paul Moore (3):

      skb: wrap skb_shared_info->destructor_arg access with set and get functions
      net: wrap skb->secmark access with set and get functions
      net: move sk_buff->secmark into a security blob in skb_shared_info
 drivers/net/macvtap.c             |    2 -
 drivers/net/tun.c                 |    2 -
 include/linux/security.h          |   91 ++++++++++++++++++++++++++-
 include/linux/skbuff.h            |   73 ++++++++++++++-------
 net/core/skbuff.c                 |   28 +++++++-
 net/ipv4/ip_output.c              |    2 -
 net/ipv6/ip6_output.c             |    2 -
 net/netfilter/xt_AUDIT.c          |    8 +-
 net/netfilter/xt_CONNSECMARK.c    |   10 ++-
 net/netfilter/xt_SECMARK.c        |    2 -
 net/packet/af_packet.c            |    4 +
 security/capability.c             |   38 +++++++++++
 security/security.c               |   63 +++++++++++++++++++
 security/selinux/hooks.c          |  126 +++++++++++++++++++++++++++++++++----
 security/selinux/include/objsec.h |    6 ++
 15 files changed, 397 insertions(+), 60 deletions(-)
author
#1 lore raw

RFC PATCH 1/3 skb: wrap skb_shared_info->destructor_arg access with set and get functions

Abstract away direct access to the "destructor_arg" field in the skb_shared_info struct via skb_destructor_arg_{set,get}() which is defined as a static inline in include/linux/skbuff.h.

---
 drivers/net/macvtap.c  |    2 +-
 drivers/net/tun.c      |    2 +-
 include/linux/skbuff.h |   19 +++++++++++++++++++
 net/core/skbuff.c      |    6 +++---
 net/packet/af_packet.c |    4 ++--
 5 files changed, 26 insertions(+), 7 deletions(-)
drivers/net/macvtap.c +1 -1
index a449439..8179df0 100644
@@ -729,7 +729,7 @@ static ssize_t macvtap_get_user(struct macvtap_queue *q, struct msghdr *m,
 	vlan = rcu_dereference_bh(q->vlan);
 	/* copy skb_ubuf_info for callback when skb has no error */
 	if (zerocopy) {
-		skb_shinfo(skb)->destructor_arg = m->msg_control;
+		skb_destructor_arg_set(skb, m->msg_control);
 		skb_shinfo(skb)->tx_flags |= SKBTX_DEV_ZEROCOPY;
 		skb_shinfo(skb)->tx_flags |= SKBTX_SHARED_FRAG;
 	}
drivers/net/tun.c +1 -1
index b7c457a..3cea09d 100644
@@ -1199,7 +1199,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,
 
 	/* copy skb_ubuf_info for callback when skb has no error */
 	if (zerocopy) {
-		skb_shinfo(skb)->destructor_arg = msg_control;
+		skb_destructor_arg_set(skb, msg_control);
 		skb_shinfo(skb)->tx_flags |= SKBTX_DEV_ZEROCOPY;
 		skb_shinfo(skb)->tx_flags |= SKBTX_SHARED_FRAG;
 	}
include/linux/skbuff.h +19 -0
index b8292d8..0998af3 100644
@@ -696,6 +696,25 @@ static inline unsigned int skb_end_offset(const struct sk_buff *skb)
 /* Internal */
 #define skb_shinfo(SKB)	((struct skb_shared_info *)(skb_end_pointer(SKB)))
 
+/**
+ *	skb_destructor_arg_set - set the packet's destructor argument
+ *	@skb: packet
+ *	@ptr: pointer to the destructor argument
+ */
+static inline void skb_destructor_arg_set(struct sk_buff *skb, void *ptr)
+{
+	skb_shinfo(skb)->destructor_arg = ptr;
+}
+
+/**
+ *	skb_destructor_arg_get - return the packet's destructor argument
+ *	@skb: packet
+ */
+static inline void *skb_destructor_arg_get(const struct sk_buff *skb)
+{
+	return skb_shinfo(skb)->destructor_arg;
+}
+
 static inline struct skb_shared_hwtstamps *skb_hwtstamps(struct sk_buff *skb)
 {
 	return &skb_shinfo(skb)->hwtstamps;
net/core/skbuff.c +3 -3
index 33245ef..e4fa549 100644
@@ -506,7 +506,7 @@ static void skb_release_data(struct sk_buff *skb)
 		if (skb_shinfo(skb)->tx_flags & SKBTX_DEV_ZEROCOPY) {
 			struct ubuf_info *uarg;
 
-			uarg = skb_shinfo(skb)->destructor_arg;
+			uarg = skb_destructor_arg_get(skb);
 			if (uarg->callback)
 				uarg->callback(uarg, true);
 		}
@@ -635,7 +635,7 @@ void skb_tx_error(struct sk_buff *skb)
 	if (skb_shinfo(skb)->tx_flags & SKBTX_DEV_ZEROCOPY) {
 		struct ubuf_info *uarg;
 
-		uarg = skb_shinfo(skb)->destructor_arg;
+		uarg = skb_destructor_arg_get(skb);
 		if (uarg->callback)
 			uarg->callback(uarg, false);
 		skb_shinfo(skb)->tx_flags &= ~SKBTX_DEV_ZEROCOPY;
@@ -782,7 +782,7 @@ int skb_copy_ubufs(struct sk_buff *skb, gfp_t gfp_mask)
 	int i;
 	int num_frags = skb_shinfo(skb)->nr_frags;
 	struct page *page, *head = NULL;
-	struct ubuf_info *uarg = skb_shinfo(skb)->destructor_arg;
+	struct ubuf_info *uarg = skb_destructor_arg_get(skb);
 
 	for (i = 0; i < num_frags; i++) {
 		u8 *vaddr;
net/packet/af_packet.c +2 -2
index 1d6793d..32f186b 100644
@@ -1833,7 +1833,7 @@ static void tpacket_destruct_skb(struct sk_buff *skb)
 	void *ph;
 
 	if (likely(po->tx_ring.pg_vec)) {
-		ph = skb_shinfo(skb)->destructor_arg;
+		ph = skb_destructor_arg_get(skb);
 		BUG_ON(atomic_read(&po->tx_ring.pending) == 0);
 		atomic_dec(&po->tx_ring.pending);
 		__packet_set_status(po, ph, TP_STATUS_AVAILABLE);
@@ -1863,7 +1863,7 @@ static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb,
 	skb->dev = dev;
 	skb->priority = po->sk.sk_priority;
 	skb->mark = po->sk.sk_mark;
-	skb_shinfo(skb)->destructor_arg = ph.raw;
+	skb_destructor_arg_set(skb, ph.raw);
 
 	switch (po->tp_version) {
 	case TPACKET_V2:
author
#2 lore raw

RFC PATCH 2/3 net: wrap skb->secmark access with set and get functions

In reply to Paul Moore

Abstract away direct access to the "secmark" field in the sk_buff struct via the LSM hooks security_skb_secmark_{get,set}().

---
 include/linux/security.h       |   20 +++++++++++++++++++-
 net/netfilter/xt_CONNSECMARK.c |   10 ++++++----
 net/netfilter/xt_SECMARK.c     |    2 +-
 security/capability.c          |   10 ++++++++++
 security/security.c            |   11 +++++++++++
 security/selinux/hooks.c       |   22 +++++++++++++++++-----
 6 files changed, 64 insertions(+), 11 deletions(-)
include/linux/security.h +19 -1
index 032c366..464f123 100644
@@ -1016,6 +1016,13 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
  *	This hook sets the packet's owning sock.
  *	@skb is the packet.
  *	@sk the sock which owns the packet.
+ * @skb_secmark_set:
+ *	This hook sets the secmark on a packet.
+ *	@skb the packet.
+ *	@secmark the secmark value.
+ * @skb_secmark_get:
+ *	This hook returns the packet's secmark.
+ *	@skb the packet.
  *
  * Security hooks for XFRM operations.
  *
@@ -1643,6 +1650,8 @@ struct security_operations {
 	int (*tun_dev_attach) (struct sock *sk, void *security);
 	int (*tun_dev_open) (void *security);
 	void (*skb_owned_by) (struct sk_buff *skb, struct sock *sk);
+	void (*skb_secmark_set) (struct sk_buff *skb, u32 secmark);
+	u32 (*skb_secmark_get) (const struct sk_buff *skb);
 #endif	/* CONFIG_SECURITY_NETWORK */
 
 #ifdef CONFIG_SECURITY_NETWORK_XFRM
@@ -2592,8 +2601,9 @@ int security_tun_dev_create(void);
 int security_tun_dev_attach_queue(void *security);
 int security_tun_dev_attach(struct sock *sk, void *security);
 int security_tun_dev_open(void *security);
-
 void security_skb_owned_by(struct sk_buff *skb, struct sock *sk);
+void security_skb_secmark_set(struct sk_buff *skb, u32 secmark);
+u32 security_skb_secmark_get(const struct sk_buff *skb);
 
 #else	/* CONFIG_SECURITY_NETWORK */
 static inline int security_unix_stream_connect(struct sock *sock,
@@ -2791,6 +2801,14 @@ static inline void security_skb_owned_by(struct sk_buff *skb, struct sock *sk)
 {
 }
 
+static inline void security_skb_secmark_set(struct sk_buff *skb, u32 secmark)
+{
+}
+
+static inline u32 security_skb_secmark_get(const struct sk_buff *skb)
+{
+	return 0;
+}
 #endif	/* CONFIG_SECURITY_NETWORK */
 
 #ifdef CONFIG_SECURITY_NETWORK_XFRM
net/netfilter/xt_CONNSECMARK.c +6 -4
index e04dc28..2201c1b 100644
@@ -35,13 +35,15 @@ MODULE_ALIAS("ip6t_CONNSECMARK");
  */
 static void secmark_save(const struct sk_buff *skb)
 {
-	if (skb->secmark) {
+	u32 secmark = security_skb_secmark_get(skb);
+
+	if (secmark) {
 		struct nf_conn *ct;
 		enum ip_conntrack_info ctinfo;
 
 		ct = nf_ct_get(skb, &ctinfo);
 		if (ct && !ct->secmark) {
-			ct->secmark = skb->secmark;
+			ct->secmark = secmark;
 			nf_conntrack_event_cache(IPCT_SECMARK, ct);
 		}
 	}
@@ -53,13 +55,13 @@ static void secmark_save(const struct sk_buff *skb)
  */
 static void secmark_restore(struct sk_buff *skb)
 {
-	if (!skb->secmark) {
+	if (!security_skb_secmark_get(skb)) {
 		const struct nf_conn *ct;
 		enum ip_conntrack_info ctinfo;
 
 		ct = nf_ct_get(skb, &ctinfo);
 		if (ct && ct->secmark)
-			skb->secmark = ct->secmark;
+			security_skb_secmark_set(skb, ct->secmark);
 	}
 }
 
net/netfilter/xt_SECMARK.c +1 -1
index 9faf5e0..ed20d8e 100644
@@ -45,7 +45,7 @@ secmark_tg(struct sk_buff *skb, const struct xt_action_param *par)
 		BUG();
 	}
 
-	skb->secmark = secmark;
+	security_skb_secmark_set(skb, secmark);
 	return XT_CONTINUE;
 }
 
security/capability.c +10 -0
index 6783c3e..b7c0ef2 100644
@@ -742,6 +742,14 @@ static void cap_skb_owned_by(struct sk_buff *skb, struct sock *sk)
 {
 }
 
+static void cap_skb_secmark_set(struct sk_buff *skb, u32 secmark)
+{
+}
+
+static u32 cap_skb_secmark_get(const struct sk_buff *skb)
+{
+	return 0;
+}
 #endif	/* CONFIG_SECURITY_NETWORK */
 
 #ifdef CONFIG_SECURITY_NETWORK_XFRM
@@ -1077,6 +1085,8 @@ void __init security_fixup_ops(struct security_operations *ops)
 	set_to_cap_if_null(ops, tun_dev_attach_queue);
 	set_to_cap_if_null(ops, tun_dev_attach);
 	set_to_cap_if_null(ops, skb_owned_by);
+	set_to_cap_if_null(ops, skb_secmark_set);
+	set_to_cap_if_null(ops, skb_secmark_get);
 #endif	/* CONFIG_SECURITY_NETWORK */
 #ifdef CONFIG_SECURITY_NETWORK_XFRM
 	set_to_cap_if_null(ops, xfrm_policy_alloc_security);
security/security.c +11 -0
index 03f248b..7e2b7c7 100644
@@ -1295,6 +1295,17 @@ void security_skb_owned_by(struct sk_buff *skb, struct sock *sk)
 	security_ops->skb_owned_by(skb, sk);
 }
 
+void security_skb_secmark_set(struct sk_buff *skb, u32 secmark)
+{
+	security_ops->skb_secmark_set(skb, secmark);
+}
+EXPORT_SYMBOL(security_skb_secmark_set);
+
+u32 security_skb_secmark_get(const struct sk_buff *skb)
+{
+	return security_ops->skb_secmark_get(skb);
+}
+EXPORT_SYMBOL(security_skb_secmark_get);
 #endif	/* CONFIG_SECURITY_NETWORK */
 
 #ifdef CONFIG_SECURITY_NETWORK_XFRM
security/selinux/hooks.c +17 -5
index 7171a95..5021cf7 100644
@@ -3473,6 +3473,21 @@ static void selinux_task_to_inode(struct task_struct *p,
 	isec->initialized = 1;
 }
 
+static void selinux_skb_owned_by(struct sk_buff *skb, struct sock *sk)
+{
+	skb_set_owner_w(skb, sk);
+}
+
+static void selinux_skb_secmark_set(struct sk_buff *skb, u32 secmark)
+{
+	skb->secmark = secmark;
+}
+
+static u32 selinux_skb_secmark_get(const struct sk_buff *skb)
+{
+	return skb->secmark;
+}
+
 /* Returns error only if unable to parse addresses */
 static int selinux_parse_skb_ipv4(struct sk_buff *skb,
 			struct common_audit_data *ad, u8 *proto)
@@ -4364,11 +4379,6 @@ static void selinux_inet_conn_established(struct sock *sk, struct sk_buff *skb)
 	selinux_skb_peerlbl_sid(skb, family, &sksec->peer_sid);
 }
 
-static void selinux_skb_owned_by(struct sk_buff *skb, struct sock *sk)
-{
-	skb_set_owner_w(skb, sk);
-}
-
 static int selinux_secmark_relabel_packet(u32 sid)
 {
 	const struct task_security_struct *__tsec;
@@ -5671,6 +5681,8 @@ static struct security_operations selinux_ops = {
 	.tun_dev_attach =		selinux_tun_dev_attach,
 	.tun_dev_open =			selinux_tun_dev_open,
 	.skb_owned_by =			selinux_skb_owned_by,
+	.skb_secmark_set =		selinux_skb_secmark_set,
+	.skb_secmark_get =		selinux_skb_secmark_get,
 
 #ifdef CONFIG_SECURITY_NETWORK_XFRM
 	.xfrm_policy_alloc_security =	selinux_xfrm_policy_alloc,
author
#3 lore raw

RFC PATCH 3/3 net: move sk_buff->secmark into a security blob in skb_shared_info

In reply to Paul Moore

This patch moves the "secmark" field from where it currently lives in the sk_buff struct to a newly overloaded field in the skb_shared_info struct. There are two main reasons for making this change 1) we shrink sk_buff from 256 bytes to 248 bytes (on x86_64) (the sk_buff breakdown is shown below) and 2) we gain a LSM security blob.

There are two catches to this change 1) the secmark value is now the same for a sk_buff and all its associated clones and 2) we overload the "destructor_arg" field in the skb_shared_info struct. The secmark/clone issue is not a problem as SELinux, the only user, keeps the secmark value consistent across clone calls and it seems reasonable that any other LSM using secmark would do the same as the LSM security attributes are generally tied to the packet's data and not the sk_buff metadata. The overloading issue is a bit more awkward, but with the sk_buff and skb_shared_info structures already carefully pruned for size, adding a new field is not an option (so says DaveM) make replacement or overloading of an existing field the only viable options. Not finding a field suitable for replacement, this patch opts for the overloading option and choose the "destructor_arg" field due to its size and usage within the core network stack.

The "destructor_arg" overloading is done in such a way that there should be no impact when the LSM is disabled at compile time, and only an additional function call when the LSM is enabled but the hooks are not in use by the active LSM. When the LSM is enabled and the active LSM is making use of the new hooks there will be some additional overhead, but that is to be expected.

For reference, here is the breakdown of the sk_buff struct:

BEFORE:
struct sk_buff {

        struct sk_buff *           next;                 /*     0     8 */
        struct sk_buff *           prev;                 /*     8     8 */
        ktime_t                    tstamp;               /*    16     8 */
        struct sock *              sk;                   /*    24     8 */
        struct net_device *        dev;                  /*    32     8 */
        char                       cb[48];               /*    40    48 */

/* --- cacheline 1 boundary (64 bytes) was 24 bytes ago --- */
long unsigned int _skb_refdst; /* 88 8 */
struct sec_path * sp; /* 96 8 */
unsigned int len; /* 104 4 */
unsigned int data_len; /* 108 4 */
__u16 mac_len; /* 112 2 */
__u16 hdr_len; /* 114 2 */
union {
__wsum csum; /* 4 */
struct {
__u16 csum_start; /* 116 2 */
__u16 csum_offset; /* 118 2 */
}; /* 4 */
}; /* 116 4 */
__u32 priority; /* 120 4 */
int flags1_begin[0]; /* 124 0 */
__u8 local_df:1; /* 124: 7 1 */
__u8 cloned:1; /* 124: 6 1 */
__u8 ip_summed:2; /* 124: 4 1 */
__u8 nohdr:1; /* 124: 3 1 */
__u8 nfctinfo:3; /* 124: 0 1 */
__u8 pkt_type:3; /* 125: 5 1 */
__u8 fclone:2; /* 125: 3 1 */
__u8 ipvs_property:1; /* 125: 2 1 */
__u8 peeked:1; /* 125: 1 1 */
__u8 nf_trace:1; /* 125: 0 1 */

/* XXX 2 bytes hole, try to pack */

/* --- cacheline 2 boundary (128 bytes) --- */
int flags1_end[0]; /* 128 0 */
__be16 protocol; /* 128 2 */

/* XXX 6 bytes hole, try to pack */

void (*destructor)(struct sk_buff *);
/* 136 8 */
struct nf_conntrack * nfct; /* 144 8 */
struct sk_buff * nfct_reasm; /* 152 8 */
struct nf_bridge_info * nf_bridge; /* 160 8 */
int skb_iif; /* 168 4 */
__u32 rxhash; /* 172 4 */
__u16 vlan_tci; /* 176 2 */
__u16 tc_index; /* 178 2 */
__u16 tc_verd; /* 180 2 */
__u16 queue_mapping; /* 182 2 */
int flags2_begin[0]; /* 184 0 */
__u8 ndisc_nodetype:2; /* 184: 6 1 */
__u8 pfmemalloc:1; /* 184: 5 1 */
__u8 ooo_okay:1; /* 184: 4 1 */
__u8 l4_rxhash:1; /* 184: 3 1 */
__u8 wifi_acked_valid:1; /* 184: 2 1 */
__u8 wifi_acked:1; /* 184: 1 1 */
__u8 no_fcs:1; /* 184: 0 1 */
__u8 head_frag:1; /* 185: 7 1 */
__u8 encapsulation:1; /* 185: 6 1 */

/* XXX 6 bits hole, try to pack */
/* XXX 2 bytes hole, try to pack */

int flags2_end[0]; /* 188 0 */
dma_cookie_t dma_cookie; /* 188 4 */
/* --- cacheline 3 boundary (192 bytes) --- */
__u32 secmark; /* 192 4 */
union {
__u32 mark; /* 4 */
__u32 dropcount; /* 4 */
__u32 reserved_tailroom; /* 4 */
}; /* 196 4 */
sk_buff_data_t inner_transport_header; /* 200 4 */
sk_buff_data_t inner_network_header; /* 204 4 */
sk_buff_data_t transport_header; /* 208 4 */
sk_buff_data_t network_header; /* 212 4 */
sk_buff_data_t mac_header; /* 216 4 */
sk_buff_data_t tail; /* 220 4 */
sk_buff_data_t end; /* 224 4 */

/* XXX 4 bytes hole, try to pack */

unsigned char * head; /* 232 8 */
unsigned char * data; /* 240 8 */
unsigned int truesize; /* 248 4 */
atomic_t users; /* 252 4 */
/* --- cacheline 4 boundary (256 bytes) --- */

/* size: 256, cachelines: 4, members: 62 */ /* sum members: 242, holes: 4, sum holes: 14 */ /* bit holes: 1, sum bit holes: 6 bits */
};

AFTER:
struct sk_buff_test {

        struct sk_buff *           next;                 /*     0     8 */
        struct sk_buff *           prev;                 /*     8     8 */
        ktime_t                    tstamp;               /*    16     8 */
        struct sock *              sk;                   /*    24     8 */
        struct net_device *        dev;                  /*    32     8 */
        char                       cb[48];               /*    40    48 */

/* --- cacheline 1 boundary (64 bytes) was 24 bytes ago --- */
long unsigned int _skb_refdst; /* 88 8 */
struct sec_path * sp; /* 96 8 */
unsigned int len; /* 104 4 */
unsigned int data_len; /* 108 4 */
__u16 mac_len; /* 112 2 */
__u16 hdr_len; /* 114 2 */
union {
__wsum csum; /* 4 */
struct {
__u16 csum_start; /* 116 2 */
__u16 csum_offset; /* 118 2 */
}; /* 4 */
}; /* 116 4 */
__u32 priority; /* 120 4 */
int flags1_begin[0]; /* 124 0 */
__u8 local_df:1; /* 124: 7 1 */
__u8 cloned:1; /* 124: 6 1 */
__u8 ip_summed:2; /* 124: 4 1 */
__u8 nohdr:1; /* 124: 3 1 */
__u8 nfctinfo:3; /* 124: 0 1 */
__u8 pkt_type:3; /* 125: 5 1 */
__u8 fclone:2; /* 125: 3 1 */
__u8 ipvs_property:1; /* 125: 2 1 */
__u8 peeked:1; /* 125: 1 1 */
__u8 nf_trace:1; /* 125: 0 1 */

/* XXX 2 bytes hole, try to pack */

/* --- cacheline 2 boundary (128 bytes) --- */
int flags1_end[0]; /* 128 0 */
__be16 protocol; /* 128 2 */

/* XXX 6 bytes hole, try to pack */

void (*destructor)(struct sk_buff *);
/* 136 8 */
struct nf_conntrack * nfct; /* 144 8 */
struct sk_buff * nfct_reasm; /* 152 8 */
struct nf_bridge_info * nf_bridge; /* 160 8 */
int skb_iif; /* 168 4 */
__u32 rxhash; /* 172 4 */
__u16 vlan_tci; /* 176 2 */
__u16 tc_index; /* 178 2 */
__u16 tc_verd; /* 180 2 */
__u16 queue_mapping; /* 182 2 */
int flags2_begin[0]; /* 184 0 */
__u8 ndisc_nodetype:2; /* 184: 6 1 */
__u8 pfmemalloc:1; /* 184: 5 1 */
__u8 ooo_okay:1; /* 184: 4 1 */
__u8 l4_rxhash:1; /* 184: 3 1 */
__u8 wifi_acked_valid:1; /* 184: 2 1 */
__u8 wifi_acked:1; /* 184: 1 1 */
__u8 no_fcs:1; /* 184: 0 1 */
__u8 head_frag:1; /* 185: 7 1 */
__u8 encapsulation:1; /* 185: 6 1 */

/* XXX 6 bits hole, try to pack */
/* XXX 2 bytes hole, try to pack */

int flags2_end[0]; /* 188 0 */
dma_cookie_t dma_cookie; /* 188 4 */
/* --- cacheline 3 boundary (192 bytes) --- */ union {
__u32 mark; /* 4 */
__u32 dropcount; /* 4 */
__u32 reserved_tailroom; /* 4 */
}; /* 192 4 */
sk_buff_data_t inner_transport_header; /* 196 4 */
sk_buff_data_t inner_network_header; /* 200 4 */
sk_buff_data_t transport_header; /* 204 4 */
sk_buff_data_t network_header; /* 208 4 */
sk_buff_data_t mac_header; /* 212 4 */
sk_buff_data_t tail; /* 216 4 */
sk_buff_data_t end; /* 220 4 */
unsigned char * head; /* 224 8 */
unsigned char * data; /* 232 8 */
unsigned int truesize; /* 240 4 */
atomic_t users; /* 244 4 */

/* size: 248, cachelines: 4, members: 61 */ /* sum members: 238, holes: 3, sum holes: 10 */ /* bit holes: 1, sum bit holes: 6 bits */ /* last cacheline: 56 bytes */
};

---
 include/linux/security.h          |   71 ++++++++++++++++++++++++
 include/linux/skbuff.h            |   58 ++++++++++----------
 net/core/skbuff.c                 |   22 +++++++-
 net/ipv4/ip_output.c              |    2 -
 net/ipv6/ip6_output.c             |    2 -
 net/netfilter/xt_AUDIT.c          |    8 +--
 security/capability.c             |   28 ++++++++++
 security/security.c               |   52 ++++++++++++++++++
 security/selinux/hooks.c          |  108 ++++++++++++++++++++++++++++++++++---
 security/selinux/include/objsec.h |    6 ++
 10 files changed, 311 insertions(+), 46 deletions(-)
include/linux/security.h +70 -1
index 464f123..53484af 100644
@@ -19,6 +19,10 @@
  *
  */
 
+/* We include linux/skbuff.h at the very top to prevent problems with both
+ * linux/skbuff.h and linux/security.h including each other */
+#include <linux/skbuff.h>
+
 #ifndef __LINUX_SECURITY_H
 #define __LINUX_SECURITY_H
 
@@ -98,7 +102,6 @@ extern int cap_task_setnice(struct task_struct *p, int nice);
 extern int cap_vm_enough_memory(struct mm_struct *mm, long pages);
 
 struct msghdr;
-struct sk_buff;
 struct sock;
 struct sockaddr;
 struct socket;
@@ -1012,10 +1015,32 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
  *	This hook can be used by the module to update any security state
  *	associated with the TUN device's security structure.
  *	@security pointer to the TUN devices's security structure.
+ * @skb_alloc:
+ *	This hook is called when a new packet is allocated and should be used
+ *	to allocate any per-packet security state.
+ *	@skb the packet
+ *	@gfp_mask allocation mask.
+ *	@flags the flags passed to __alloc_skb().
+ *	@node NUMA node, set to -1 if NUMA information is not available.
+ * @skb_free:
+ *	This hook is called when a packet is freed and should be used to
+ *	release any per-packet security state.
+ *	@skb the packet.
+ * @skb_security_copy:
+ *	This hook copies the security metadata from one packet to another.
+ *	@dst the destination packet.
+ *	@src the source packet.
  * @skb_owned_by:
  *	This hook sets the packet's owning sock.
  *	@skb is the packet.
  *	@sk the sock which owns the packet.
+ * @skb_destructor_arg_set:
+ *	This hook sets the packet's destructor argument.
+ *	@skb packet.
+ *	@ptr pointer to the destructor argument.
+ * @skb_destructor_arg_get:
+ *	This hook returns the packet's destructor argument.
+ *	@skb the packet.
  * @skb_secmark_set:
  *	This hook sets the secmark on a packet.
  *	@skb the packet.
@@ -1649,7 +1674,12 @@ struct security_operations {
 	int (*tun_dev_attach_queue) (void *security);
 	int (*tun_dev_attach) (struct sock *sk, void *security);
 	int (*tun_dev_open) (void *security);
+	int (*skb_alloc) (struct sk_buff *skb, gfp_t gfp_mask, int flags, int node);
+	void (*skb_free) (struct sk_buff *skb);
+	void (*skb_security_copy) (struct sk_buff *dst, const struct sk_buff *src);
 	void (*skb_owned_by) (struct sk_buff *skb, struct sock *sk);
+	void (*skb_destructor_arg_set) (struct sk_buff *skb, void *ptr);
+	void *(*skb_destructor_arg_get) (const struct sk_buff *skb);
 	void (*skb_secmark_set) (struct sk_buff *skb, u32 secmark);
 	u32 (*skb_secmark_get) (const struct sk_buff *skb);
 #endif	/* CONFIG_SECURITY_NETWORK */
@@ -2601,6 +2631,13 @@ int security_tun_dev_create(void);
 int security_tun_dev_attach_queue(void *security);
 int security_tun_dev_attach(struct sock *sk, void *security);
 int security_tun_dev_open(void *security);
+int security_skb_alloc(struct sk_buff *skb, gfp_t gfp_mask, int flags, int node);
+void security_skb_free(struct sk_buff *skb);
+void security_skb_ref(struct sk_buff *skb);
+void security_skb_unref(struct sk_buff *skb);
+void security_skb_security_copy(struct sk_buff *dst, const struct sk_buff *src);
+void security_skb_destructor_arg_set(struct sk_buff *skb, void *ptr);
+void *security_skb_destructor_arg_get(const struct sk_buff *skb);
 void security_skb_owned_by(struct sk_buff *skb, struct sock *sk);
 void security_skb_secmark_set(struct sk_buff *skb, u32 secmark);
 u32 security_skb_secmark_get(const struct sk_buff *skb);
@@ -2797,10 +2834,42 @@ static inline int security_tun_dev_open(void *security)
 	return 0;
 }
 
+static inline int security_skb_alloc(struct sk_buff *skb, gfp_t gfp_mask, int flags, int node)
+{
+	return 0;
+}
+
+static inline void security_skb_free(struct sk_buff *skb)
+{
+}
+
+static inline void security_skb_ref(struct sk_buff *skb)
+{
+}
+
+static inline void security_skb_unref(struct sk_buff *skb)
+{
+}
+
+static inline void security_skb_security_copy(struct sk_buff *dst,
+					      const struct sk_buff *src)
+{
+}
+
 static inline void security_skb_owned_by(struct sk_buff *skb, struct sock *sk)
 {
 }
 
+static inline void security_skb_destructor_arg_set(struct sk_buff *skb, void *ptr)
+{
+	skb_shinfo(skb)->destructor_arg = ptr;
+}
+
+static inline void *security_skb_destructor_arg_get(const struct sk_buff *skb)
+{
+	return skb_shinfo(skb)->destructor_arg;
+}
+
 static inline void security_skb_secmark_set(struct sk_buff *skb, u32 secmark)
 {
 }
include/linux/skbuff.h +30 -28

112 lines not on this page · raw message

net/core/skbuff.c +20 -2
index e4fa549..9d4ea5f 100644
@@ -70,6 +70,7 @@
 #include <asm/uaccess.h>
 #include <trace/events/skb.h>
 #include <linux/highmem.h>
+#include <linux/security.h>
 
 struct kmem_cache *skbuff_head_cache __read_mostly;
 static struct kmem_cache *skbuff_fclone_cache __read_mostly;
@@ -271,12 +272,20 @@ struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask,
 		child->fclone = SKB_FCLONE_UNAVAILABLE;
 		child->pfmemalloc = pfmemalloc;
 	}
+
+	if (security_skb_alloc(skb, gfp_mask, flags, node) < 0)
+		goto data;
+
 out:
 	return skb;
 nodata:
 	kmem_cache_free(cache, skb);
 	skb = NULL;
 	goto out;
+data:
+	kfree_skb(skb);
+	skb = NULL;
+	goto out;
 }
 EXPORT_SYMBOL(__alloc_skb);
 
@@ -328,7 +337,14 @@ struct sk_buff *build_skb(void *data, unsigned int frag_size)
 	atomic_set(&shinfo->dataref, 1);
 	kmemcheck_annotate_variable(shinfo->destructor_arg);
 
+	if (security_skb_alloc(skb, GFP_ATOMIC, 0, -1) < 0)
+		goto free_skb;
+
 	return skb;
+
+free_skb:
+	kmem_cache_free(skbuff_head_cache, skb);
+	return NULL;
 }
 EXPORT_SYMBOL(build_skb);
 
@@ -514,6 +530,8 @@ static void skb_release_data(struct sk_buff *skb)
 		if (skb_has_frag_list(skb))
 			skb_drop_fraglist(skb);
 
+		security_skb_free(skb);
+
 		skb_free_head(skb);
 	}
 }
@@ -707,8 +725,6 @@ static void __copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
 #endif
 #endif
 	new->vlan_tci		= old->vlan_tci;
-
-	skb_copy_secmark(new, old);
 }
 
 /*
@@ -1062,6 +1078,8 @@ int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail,
 		if (skb_has_frag_list(skb))
 			skb_clone_fraglist(skb);
 
+		security_skb_ref(skb);
+
 		skb_release_data(skb);
 	} else {
 		skb_free_head(skb);
net/ipv4/ip_output.c +1 -1
index 5e12dca..c2fb026 100644
@@ -437,7 +437,7 @@ static void ip_copy_metadata(struct sk_buff *to, struct sk_buff *from)
 #if defined(CONFIG_IP_VS) || defined(CONFIG_IP_VS_MODULE)
 	to->ipvs_property = from->ipvs_property;
 #endif
-	skb_copy_secmark(to, from);
+	security_skb_security_copy(to, from);
 }
 
 /*
net/ipv6/ip6_output.c +1 -1
index 155eccf..4d176b7 100644
@@ -520,7 +520,7 @@ static void ip6_copy_metadata(struct sk_buff *to, struct sk_buff *from)
 #if IS_ENABLED(CONFIG_NETFILTER_XT_TARGET_TRACE)
 	to->nf_trace = from->nf_trace;
 #endif
-	skb_copy_secmark(to, from);
+	security_skb_security_copy(to, from);
 }
 
 int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
net/netfilter/xt_AUDIT.c +4 -4
index 3228d7f..4fa6d27 100644
@@ -123,6 +123,7 @@ audit_tg(struct sk_buff *skb, const struct xt_action_param *par)
 {
 	const struct xt_audit_info *info = par->targinfo;
 	struct audit_buffer *ab;
+	u32 secmark;
 
 	if (audit_enabled == 0)
 		goto errout;
@@ -167,10 +168,9 @@ audit_tg(struct sk_buff *skb, const struct xt_action_param *par)
 		break;
 	}
 
-#ifdef CONFIG_NETWORK_SECMARK
-	if (skb->secmark)
-		audit_log_secctx(ab, skb->secmark);
-#endif
+	secmark = security_skb_secmark_get(skb);
+	if (secmark)
+		audit_log_secctx(ab, secmark);
 
 	audit_log_end(ab);
 
security/capability.c +28 -0
index b7c0ef2..1ff3b6f 100644
@@ -738,10 +738,33 @@ static int cap_tun_dev_open(void *security)
 	return 0;
 }
 
+static int cap_skb_alloc(struct sk_buff *skb, gfp_t gfp_mask, int flags, int node)
+{
+	return 0;
+}
+
+static void cap_skb_free(struct sk_buff *skb)
+{
+}
+
+static void cap_skb_security_copy(struct sk_buff *dst, const struct sk_buff *src)
+{
+}
+
 static void cap_skb_owned_by(struct sk_buff *skb, struct sock *sk)
 {
 }
 
+static void cap_skb_destructor_arg_set(struct sk_buff *skb, void *ptr)
+{
+	skb_shinfo(skb)->destructor_arg = ptr;
+}
+
+static void *cap_skb_destructor_arg_get(const struct sk_buff *skb)
+{
+	return skb_shinfo(skb)->destructor_arg;
+}
+
 static void cap_skb_secmark_set(struct sk_buff *skb, u32 secmark)
 {
 }
@@ -1084,7 +1107,12 @@ void __init security_fixup_ops(struct security_operations *ops)
 	set_to_cap_if_null(ops, tun_dev_open);
 	set_to_cap_if_null(ops, tun_dev_attach_queue);
 	set_to_cap_if_null(ops, tun_dev_attach);
+	set_to_cap_if_null(ops, skb_alloc);
+	set_to_cap_if_null(ops, skb_free);
+	set_to_cap_if_null(ops, skb_security_copy);
 	set_to_cap_if_null(ops, skb_owned_by);
+	set_to_cap_if_null(ops, skb_destructor_arg_set);
+	set_to_cap_if_null(ops, skb_destructor_arg_get);
 	set_to_cap_if_null(ops, skb_secmark_set);
 	set_to_cap_if_null(ops, skb_secmark_get);
 #endif	/* CONFIG_SECURITY_NETWORK */
security/security.c +52 -0

69 lines not on this page · raw message

security/selinux/hooks.c +99 -9

174 lines not on this page · raw message

security/selinux/include/objsec.h +6 -0
index aa47bca..7ec3623 100644
@@ -110,6 +110,12 @@ struct sk_security_struct {
 	u16 sclass;			/* sock security class */
 };
 
+#define skb_security(SKB) \
+	((struct skb_security_struct *)(&(skb_optinfo(SKB)->security)))
+struct skb_security_struct {
+	int secmark;
+};
+
 struct tun_security_struct {
 	u32 sid;			/* SID for the tun device sockets */
 };