Skip to content

Commit 89aa361

Browse files
WOnder93davem330
authored andcommitted
cipso: make cipso_v4_skbuff_delattr() fully remove the CIPSO options
As the comment in this function says, the code currently just clears the CIPSO part with IPOPT_NOP, rather than removing it completely and trimming the packet. The other cipso_v4_*_delattr() functions, however, do the proper removal and also calipso_skbuff_delattr() makes an effort to remove the CALIPSO options instead of replacing them with padding. Some routers treat IPv4 packets with anything (even NOPs) in the option header as a special case and take them through a slower processing path. Consequently, hardening guides such as STIG recommend to configure such routers to drop packets with non-empty IP option headers [1][2]. Thus, users might expect NetLabel to produce packets with minimal padding (or at least with no padding when no actual options are present). Implement the proper option removal to address this and to be closer to what the peer functions do. [1] https://www.stigviewer.com/stig/juniper_router_rtr/2019-09-27/finding/V-90937 [2] https://www.stigviewer.com/stig/cisco_ios_xe_router_rtr/2021-03-26/finding/V-217001 Signed-off-by: Ondrej Mosnacek <omosnace@redhat.com> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent 9f36169 commit 89aa361

File tree

1 file changed

+54
-25
lines changed

1 file changed

+54
-25
lines changed

net/ipv4/cipso_ipv4.c

Lines changed: 54 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1810,6 +1810,29 @@ static int cipso_v4_genopt(unsigned char *buf, u32 buf_len,
18101810
return CIPSO_V4_HDR_LEN + ret_val;
18111811
}
18121812

1813+
static int cipso_v4_get_actual_opt_len(const unsigned char *data, int len)
1814+
{
1815+
int iter = 0, optlen = 0;
1816+
1817+
/* determining the new total option length is tricky because of
1818+
* the padding necessary, the only thing i can think to do at
1819+
* this point is walk the options one-by-one, skipping the
1820+
* padding at the end to determine the actual option size and
1821+
* from there we can determine the new total option length
1822+
*/
1823+
while (iter < len) {
1824+
if (data[iter] == IPOPT_END) {
1825+
break;
1826+
} else if (data[iter] == IPOPT_NOP) {
1827+
iter++;
1828+
} else {
1829+
iter += data[iter + 1];
1830+
optlen = iter;
1831+
}
1832+
}
1833+
return optlen;
1834+
}
1835+
18131836
/**
18141837
* cipso_v4_sock_setattr - Add a CIPSO option to a socket
18151838
* @sk: the socket
@@ -1986,7 +2009,6 @@ static int cipso_v4_delopt(struct ip_options_rcu __rcu **opt_ptr)
19862009
u8 cipso_len;
19872010
u8 cipso_off;
19882011
unsigned char *cipso_ptr;
1989-
int iter;
19902012
int optlen_new;
19912013

19922014
cipso_off = opt->opt.cipso - sizeof(struct iphdr);
@@ -2006,23 +2028,8 @@ static int cipso_v4_delopt(struct ip_options_rcu __rcu **opt_ptr)
20062028
memmove(cipso_ptr, cipso_ptr + cipso_len,
20072029
opt->opt.optlen - cipso_off - cipso_len);
20082030

2009-
/* determining the new total option length is tricky because of
2010-
* the padding necessary, the only thing i can think to do at
2011-
* this point is walk the options one-by-one, skipping the
2012-
* padding at the end to determine the actual option size and
2013-
* from there we can determine the new total option length */
2014-
iter = 0;
2015-
optlen_new = 0;
2016-
while (iter < opt->opt.optlen) {
2017-
if (opt->opt.__data[iter] == IPOPT_END) {
2018-
break;
2019-
} else if (opt->opt.__data[iter] == IPOPT_NOP) {
2020-
iter++;
2021-
} else {
2022-
iter += opt->opt.__data[iter + 1];
2023-
optlen_new = iter;
2024-
}
2025-
}
2031+
optlen_new = cipso_v4_get_actual_opt_len(opt->opt.__data,
2032+
opt->opt.optlen);
20262033
hdr_delta = opt->opt.optlen;
20272034
opt->opt.optlen = (optlen_new + 3) & ~3;
20282035
hdr_delta -= opt->opt.optlen;
@@ -2242,7 +2249,8 @@ int cipso_v4_skbuff_setattr(struct sk_buff *skb,
22422249
*/
22432250
int cipso_v4_skbuff_delattr(struct sk_buff *skb)
22442251
{
2245-
int ret_val;
2252+
int ret_val, cipso_len, hdr_len_actual, new_hdr_len_actual, new_hdr_len,
2253+
hdr_len_delta;
22462254
struct iphdr *iph;
22472255
struct ip_options *opt = &IPCB(skb)->opt;
22482256
unsigned char *cipso_ptr;
@@ -2255,16 +2263,37 @@ int cipso_v4_skbuff_delattr(struct sk_buff *skb)
22552263
if (ret_val < 0)
22562264
return ret_val;
22572265

2258-
/* the easiest thing to do is just replace the cipso option with noop
2259-
* options since we don't change the size of the packet, although we
2260-
* still need to recalculate the checksum */
2261-
22622266
iph = ip_hdr(skb);
22632267
cipso_ptr = (unsigned char *)iph + opt->cipso;
2264-
memset(cipso_ptr, IPOPT_NOOP, cipso_ptr[1]);
2268+
cipso_len = cipso_ptr[1];
2269+
2270+
hdr_len_actual = sizeof(struct iphdr) +
2271+
cipso_v4_get_actual_opt_len((unsigned char *)(iph + 1),
2272+
opt->optlen);
2273+
new_hdr_len_actual = hdr_len_actual - cipso_len;
2274+
new_hdr_len = (new_hdr_len_actual + 3) & ~3;
2275+
hdr_len_delta = (iph->ihl << 2) - new_hdr_len;
2276+
2277+
/* 1. shift any options after CIPSO to the left */
2278+
memmove(cipso_ptr, cipso_ptr + cipso_len,
2279+
new_hdr_len_actual - opt->cipso);
2280+
/* 2. move the whole IP header to its new place */
2281+
memmove((unsigned char *)iph + hdr_len_delta, iph, new_hdr_len_actual);
2282+
/* 3. adjust the skb layout */
2283+
skb_pull(skb, hdr_len_delta);
2284+
skb_reset_network_header(skb);
2285+
iph = ip_hdr(skb);
2286+
/* 4. re-fill new padding with IPOPT_END (may now be longer) */
2287+
memset((unsigned char *)iph + new_hdr_len_actual, IPOPT_END,
2288+
new_hdr_len - new_hdr_len_actual);
2289+
2290+
opt->optlen -= hdr_len_delta;
22652291
opt->cipso = 0;
22662292
opt->is_changed = 1;
2267-
2293+
if (hdr_len_delta != 0) {
2294+
iph->ihl = new_hdr_len >> 2;
2295+
iph_set_totlen(iph, skb->len);
2296+
}
22682297
ip_send_check(iph);
22692298

22702299
return 0;

0 commit comments

Comments
 (0)