|
@@ -62,6 +62,11 @@
|
|
|
*/
|
|
|
#define CALIPSO_OPT_LEN_MAX_WITH_PAD (3 + CALIPSO_OPT_LEN_MAX + 7)
|
|
|
|
|
|
+ /* Maximium size of u32 aligned buffer required to hold calipso
|
|
|
+ * option. Max of 3 initial pad bytes starting from buffer + 3.
|
|
|
+ * i.e. the worst case is when the previous tlv finishes on 4n + 3.
|
|
|
+ */
|
|
|
+#define CALIPSO_MAX_BUFFER (6 + CALIPSO_OPT_LEN_MAX)
|
|
|
|
|
|
/* List of available DOI definitions */
|
|
|
static DEFINE_SPINLOCK(calipso_doi_list_lock);
|
|
@@ -973,6 +978,162 @@ static void calipso_req_delattr(struct request_sock *req)
|
|
|
kfree(new);
|
|
|
}
|
|
|
|
|
|
+/* skbuff functions.
|
|
|
+ */
|
|
|
+
|
|
|
+/**
|
|
|
+ * calipso_skbuff_optptr - Find the CALIPSO option in the packet
|
|
|
+ * @skb: the packet
|
|
|
+ *
|
|
|
+ * Description:
|
|
|
+ * Parse the packet's IP header looking for a CALIPSO option. Returns a pointer
|
|
|
+ * to the start of the CALIPSO option on success, NULL if one if not found.
|
|
|
+ *
|
|
|
+ */
|
|
|
+static unsigned char *calipso_skbuff_optptr(const struct sk_buff *skb)
|
|
|
+{
|
|
|
+ const struct ipv6hdr *ip6_hdr = ipv6_hdr(skb);
|
|
|
+ int offset;
|
|
|
+
|
|
|
+ if (ip6_hdr->nexthdr != NEXTHDR_HOP)
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ offset = ipv6_find_tlv(skb, sizeof(*ip6_hdr), IPV6_TLV_CALIPSO);
|
|
|
+ if (offset >= 0)
|
|
|
+ return (unsigned char *)ip6_hdr + offset;
|
|
|
+
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * calipso_skbuff_setattr - Set the CALIPSO option on a packet
|
|
|
+ * @skb: the packet
|
|
|
+ * @doi_def: the CALIPSO DOI to use
|
|
|
+ * @secattr: the security attributes
|
|
|
+ *
|
|
|
+ * Description:
|
|
|
+ * Set the CALIPSO option on the given packet based on the security attributes.
|
|
|
+ * Returns a pointer to the IP header on success and NULL on failure.
|
|
|
+ *
|
|
|
+ */
|
|
|
+static int calipso_skbuff_setattr(struct sk_buff *skb,
|
|
|
+ const struct calipso_doi *doi_def,
|
|
|
+ const struct netlbl_lsm_secattr *secattr)
|
|
|
+{
|
|
|
+ int ret_val;
|
|
|
+ struct ipv6hdr *ip6_hdr;
|
|
|
+ struct ipv6_opt_hdr *hop;
|
|
|
+ unsigned char buf[CALIPSO_MAX_BUFFER];
|
|
|
+ int len_delta, new_end, pad;
|
|
|
+ unsigned int start, end;
|
|
|
+
|
|
|
+ ip6_hdr = ipv6_hdr(skb);
|
|
|
+ if (ip6_hdr->nexthdr == NEXTHDR_HOP) {
|
|
|
+ hop = (struct ipv6_opt_hdr *)(ip6_hdr + 1);
|
|
|
+ ret_val = calipso_opt_find(hop, &start, &end);
|
|
|
+ if (ret_val && ret_val != -ENOENT)
|
|
|
+ return ret_val;
|
|
|
+ } else {
|
|
|
+ start = 0;
|
|
|
+ end = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ memset(buf, 0, sizeof(buf));
|
|
|
+ ret_val = calipso_genopt(buf, start & 3, sizeof(buf), doi_def, secattr);
|
|
|
+ if (ret_val < 0)
|
|
|
+ return ret_val;
|
|
|
+
|
|
|
+ new_end = start + ret_val;
|
|
|
+ /* At this point new_end aligns to 4n, so (new_end & 4) pads to 8n */
|
|
|
+ pad = ((new_end & 4) + (end & 7)) & 7;
|
|
|
+ len_delta = new_end - (int)end + pad;
|
|
|
+ ret_val = skb_cow(skb, skb_headroom(skb) + len_delta);
|
|
|
+ if (ret_val < 0)
|
|
|
+ return ret_val;
|
|
|
+
|
|
|
+ if (len_delta) {
|
|
|
+ if (len_delta > 0)
|
|
|
+ skb_push(skb, len_delta);
|
|
|
+ else
|
|
|
+ skb_pull(skb, -len_delta);
|
|
|
+ memmove((char *)ip6_hdr - len_delta, ip6_hdr,
|
|
|
+ sizeof(*ip6_hdr) + start);
|
|
|
+ skb_reset_network_header(skb);
|
|
|
+ ip6_hdr = ipv6_hdr(skb);
|
|
|
+ }
|
|
|
+
|
|
|
+ hop = (struct ipv6_opt_hdr *)(ip6_hdr + 1);
|
|
|
+ if (start == 0) {
|
|
|
+ struct ipv6_opt_hdr *new_hop = (struct ipv6_opt_hdr *)buf;
|
|
|
+
|
|
|
+ new_hop->nexthdr = ip6_hdr->nexthdr;
|
|
|
+ new_hop->hdrlen = len_delta / 8 - 1;
|
|
|
+ ip6_hdr->nexthdr = NEXTHDR_HOP;
|
|
|
+ } else {
|
|
|
+ hop->hdrlen += len_delta / 8;
|
|
|
+ }
|
|
|
+ memcpy((char *)hop + start, buf + (start & 3), new_end - start);
|
|
|
+ calipso_pad_write((unsigned char *)hop, new_end, pad);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * calipso_skbuff_delattr - Delete any CALIPSO options from a packet
|
|
|
+ * @skb: the packet
|
|
|
+ *
|
|
|
+ * Description:
|
|
|
+ * Removes any and all CALIPSO options from the given packet. Returns zero on
|
|
|
+ * success, negative values on failure.
|
|
|
+ *
|
|
|
+ */
|
|
|
+static int calipso_skbuff_delattr(struct sk_buff *skb)
|
|
|
+{
|
|
|
+ int ret_val;
|
|
|
+ struct ipv6hdr *ip6_hdr;
|
|
|
+ struct ipv6_opt_hdr *old_hop;
|
|
|
+ u32 old_hop_len, start = 0, end = 0, delta, size, pad;
|
|
|
+
|
|
|
+ if (!calipso_skbuff_optptr(skb))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ /* since we are changing the packet we should make a copy */
|
|
|
+ ret_val = skb_cow(skb, skb_headroom(skb));
|
|
|
+ if (ret_val < 0)
|
|
|
+ return ret_val;
|
|
|
+
|
|
|
+ ip6_hdr = ipv6_hdr(skb);
|
|
|
+ old_hop = (struct ipv6_opt_hdr *)(ip6_hdr + 1);
|
|
|
+ old_hop_len = ipv6_optlen(old_hop);
|
|
|
+
|
|
|
+ ret_val = calipso_opt_find(old_hop, &start, &end);
|
|
|
+ if (ret_val)
|
|
|
+ return ret_val;
|
|
|
+
|
|
|
+ if (start == sizeof(*old_hop) && end == old_hop_len) {
|
|
|
+ /* There's no other option in the header so we delete
|
|
|
+ * the whole thing. */
|
|
|
+ delta = old_hop_len;
|
|
|
+ size = sizeof(*ip6_hdr);
|
|
|
+ ip6_hdr->nexthdr = old_hop->nexthdr;
|
|
|
+ } else {
|
|
|
+ delta = (end - start) & ~7;
|
|
|
+ if (delta)
|
|
|
+ old_hop->hdrlen -= delta / 8;
|
|
|
+ pad = (end - start) & 7;
|
|
|
+ size = sizeof(*ip6_hdr) + start + pad;
|
|
|
+ calipso_pad_write((unsigned char *)old_hop, start, pad);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (delta) {
|
|
|
+ skb_pull(skb, delta);
|
|
|
+ memmove((char *)ip6_hdr + delta, ip6_hdr, size);
|
|
|
+ skb_reset_network_header(skb);
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static const struct netlbl_calipso_ops ops = {
|
|
|
.doi_add = calipso_doi_add,
|
|
|
.doi_free = calipso_doi_free,
|
|
@@ -985,6 +1146,10 @@ static const struct netlbl_calipso_ops ops = {
|
|
|
.sock_delattr = calipso_sock_delattr,
|
|
|
.req_setattr = calipso_req_setattr,
|
|
|
.req_delattr = calipso_req_delattr,
|
|
|
+ .opt_getattr = calipso_opt_getattr,
|
|
|
+ .skbuff_optptr = calipso_skbuff_optptr,
|
|
|
+ .skbuff_setattr = calipso_skbuff_setattr,
|
|
|
+ .skbuff_delattr = calipso_skbuff_delattr,
|
|
|
};
|
|
|
|
|
|
/**
|