nft_exthdr.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Copyright (c) 2008 Patrick McHardy <kaber@trash.net>
  4. *
  5. * Development of this code funded by Astaro AG (http://www.astaro.com/)
  6. */
  7. #include <asm/unaligned.h>
  8. #include <linux/kernel.h>
  9. #include <linux/netlink.h>
  10. #include <linux/netfilter.h>
  11. #include <linux/netfilter/nf_tables.h>
  12. #include <net/netfilter/nf_tables_core.h>
  13. #include <net/netfilter/nf_tables.h>
  14. #include <net/tcp.h>
  15. struct nft_exthdr {
  16. u8 type;
  17. u8 offset;
  18. u8 len;
  19. u8 op;
  20. enum nft_registers dreg:8;
  21. enum nft_registers sreg:8;
  22. u8 flags;
  23. };
  24. static unsigned int optlen(const u8 *opt, unsigned int offset)
  25. {
  26. /* Beware zero-length options: make finite progress */
  27. if (opt[offset] <= TCPOPT_NOP || opt[offset + 1] == 0)
  28. return 1;
  29. else
  30. return opt[offset + 1];
  31. }
  32. static void nft_exthdr_ipv6_eval(const struct nft_expr *expr,
  33. struct nft_regs *regs,
  34. const struct nft_pktinfo *pkt)
  35. {
  36. struct nft_exthdr *priv = nft_expr_priv(expr);
  37. u32 *dest = &regs->data[priv->dreg];
  38. unsigned int offset = 0;
  39. int err;
  40. if (pkt->skb->protocol != htons(ETH_P_IPV6))
  41. goto err;
  42. err = ipv6_find_hdr(pkt->skb, &offset, priv->type, NULL, NULL);
  43. if (priv->flags & NFT_EXTHDR_F_PRESENT) {
  44. nft_reg_store8(dest, err >= 0);
  45. return;
  46. } else if (err < 0) {
  47. goto err;
  48. }
  49. offset += priv->offset;
  50. dest[priv->len / NFT_REG32_SIZE] = 0;
  51. if (skb_copy_bits(pkt->skb, offset, dest, priv->len) < 0)
  52. goto err;
  53. return;
  54. err:
  55. regs->verdict.code = NFT_BREAK;
  56. }
  57. /* find the offset to specified option.
  58. *
  59. * If target header is found, its offset is set in *offset and return option
  60. * number. Otherwise, return negative error.
  61. *
  62. * If the first fragment doesn't contain the End of Options it is considered
  63. * invalid.
  64. */
  65. static int ipv4_find_option(struct net *net, struct sk_buff *skb,
  66. unsigned int *offset, int target)
  67. {
  68. unsigned char optbuf[sizeof(struct ip_options) + 40];
  69. struct ip_options *opt = (struct ip_options *)optbuf;
  70. struct iphdr *iph, _iph;
  71. unsigned int start;
  72. bool found = false;
  73. __be32 info;
  74. int optlen;
  75. iph = skb_header_pointer(skb, 0, sizeof(_iph), &_iph);
  76. if (!iph)
  77. return -EBADMSG;
  78. start = sizeof(struct iphdr);
  79. optlen = iph->ihl * 4 - (int)sizeof(struct iphdr);
  80. if (optlen <= 0)
  81. return -ENOENT;
  82. memset(opt, 0, sizeof(struct ip_options));
  83. /* Copy the options since __ip_options_compile() modifies
  84. * the options.
  85. */
  86. if (skb_copy_bits(skb, start, opt->__data, optlen))
  87. return -EBADMSG;
  88. opt->optlen = optlen;
  89. if (__ip_options_compile(net, opt, NULL, &info))
  90. return -EBADMSG;
  91. switch (target) {
  92. case IPOPT_SSRR:
  93. case IPOPT_LSRR:
  94. if (!opt->srr)
  95. break;
  96. found = target == IPOPT_SSRR ? opt->is_strictroute :
  97. !opt->is_strictroute;
  98. if (found)
  99. *offset = opt->srr + start;
  100. break;
  101. case IPOPT_RR:
  102. if (!opt->rr)
  103. break;
  104. *offset = opt->rr + start;
  105. found = true;
  106. break;
  107. case IPOPT_RA:
  108. if (!opt->router_alert)
  109. break;
  110. *offset = opt->router_alert + start;
  111. found = true;
  112. break;
  113. default:
  114. return -EOPNOTSUPP;
  115. }
  116. return found ? target : -ENOENT;
  117. }
  118. static void nft_exthdr_ipv4_eval(const struct nft_expr *expr,
  119. struct nft_regs *regs,
  120. const struct nft_pktinfo *pkt)
  121. {
  122. struct nft_exthdr *priv = nft_expr_priv(expr);
  123. u32 *dest = &regs->data[priv->dreg];
  124. struct sk_buff *skb = pkt->skb;
  125. unsigned int offset;
  126. int err;
  127. if (skb->protocol != htons(ETH_P_IP))
  128. goto err;
  129. err = ipv4_find_option(nft_net(pkt), skb, &offset, priv->type);
  130. if (priv->flags & NFT_EXTHDR_F_PRESENT) {
  131. nft_reg_store8(dest, err >= 0);
  132. return;
  133. } else if (err < 0) {
  134. goto err;
  135. }
  136. offset += priv->offset;
  137. dest[priv->len / NFT_REG32_SIZE] = 0;
  138. if (skb_copy_bits(pkt->skb, offset, dest, priv->len) < 0)
  139. goto err;
  140. return;
  141. err:
  142. regs->verdict.code = NFT_BREAK;
  143. }
  144. static void *
  145. nft_tcp_header_pointer(const struct nft_pktinfo *pkt,
  146. unsigned int len, void *buffer, unsigned int *tcphdr_len)
  147. {
  148. struct tcphdr *tcph;
  149. if (!pkt->tprot_set || pkt->tprot != IPPROTO_TCP)
  150. return NULL;
  151. tcph = skb_header_pointer(pkt->skb, pkt->xt.thoff, sizeof(*tcph), buffer);
  152. if (!tcph)
  153. return NULL;
  154. *tcphdr_len = __tcp_hdrlen(tcph);
  155. if (*tcphdr_len < sizeof(*tcph) || *tcphdr_len > len)
  156. return NULL;
  157. return skb_header_pointer(pkt->skb, pkt->xt.thoff, *tcphdr_len, buffer);
  158. }
  159. static void nft_exthdr_tcp_eval(const struct nft_expr *expr,
  160. struct nft_regs *regs,
  161. const struct nft_pktinfo *pkt)
  162. {
  163. u8 buff[sizeof(struct tcphdr) + MAX_TCP_OPTION_SPACE];
  164. struct nft_exthdr *priv = nft_expr_priv(expr);
  165. unsigned int i, optl, tcphdr_len, offset;
  166. u32 *dest = &regs->data[priv->dreg];
  167. struct tcphdr *tcph;
  168. u8 *opt;
  169. tcph = nft_tcp_header_pointer(pkt, sizeof(buff), buff, &tcphdr_len);
  170. if (!tcph)
  171. goto err;
  172. opt = (u8 *)tcph;
  173. for (i = sizeof(*tcph); i < tcphdr_len - 1; i += optl) {
  174. optl = optlen(opt, i);
  175. if (priv->type != opt[i])
  176. continue;
  177. if (i + optl > tcphdr_len || priv->len + priv->offset > optl)
  178. goto err;
  179. offset = i + priv->offset;
  180. if (priv->flags & NFT_EXTHDR_F_PRESENT) {
  181. *dest = 1;
  182. } else {
  183. dest[priv->len / NFT_REG32_SIZE] = 0;
  184. memcpy(dest, opt + offset, priv->len);
  185. }
  186. return;
  187. }
  188. err:
  189. if (priv->flags & NFT_EXTHDR_F_PRESENT)
  190. *dest = 0;
  191. else
  192. regs->verdict.code = NFT_BREAK;
  193. }
  194. static void nft_exthdr_tcp_set_eval(const struct nft_expr *expr,
  195. struct nft_regs *regs,
  196. const struct nft_pktinfo *pkt)
  197. {
  198. u8 buff[sizeof(struct tcphdr) + MAX_TCP_OPTION_SPACE];
  199. struct nft_exthdr *priv = nft_expr_priv(expr);
  200. unsigned int i, optl, tcphdr_len, offset;
  201. struct tcphdr *tcph;
  202. u8 *opt;
  203. tcph = nft_tcp_header_pointer(pkt, sizeof(buff), buff, &tcphdr_len);
  204. if (!tcph)
  205. return;
  206. opt = (u8 *)tcph;
  207. for (i = sizeof(*tcph); i < tcphdr_len - 1; i += optl) {
  208. union {
  209. __be16 v16;
  210. __be32 v32;
  211. } old, new;
  212. optl = optlen(opt, i);
  213. if (priv->type != opt[i])
  214. continue;
  215. if (i + optl > tcphdr_len || priv->len + priv->offset > optl)
  216. return;
  217. if (skb_ensure_writable(pkt->skb,
  218. pkt->xt.thoff + i + priv->len))
  219. return;
  220. tcph = nft_tcp_header_pointer(pkt, sizeof(buff), buff,
  221. &tcphdr_len);
  222. if (!tcph)
  223. return;
  224. offset = i + priv->offset;
  225. switch (priv->len) {
  226. case 2:
  227. old.v16 = get_unaligned((u16 *)(opt + offset));
  228. new.v16 = (__force __be16)nft_reg_load16(
  229. &regs->data[priv->sreg]);
  230. switch (priv->type) {
  231. case TCPOPT_MSS:
  232. /* increase can cause connection to stall */
  233. if (ntohs(old.v16) <= ntohs(new.v16))
  234. return;
  235. break;
  236. }
  237. if (old.v16 == new.v16)
  238. return;
  239. put_unaligned(new.v16, (u16*)(opt + offset));
  240. inet_proto_csum_replace2(&tcph->check, pkt->skb,
  241. old.v16, new.v16, false);
  242. break;
  243. case 4:
  244. new.v32 = regs->data[priv->sreg];
  245. old.v32 = get_unaligned((u32 *)(opt + offset));
  246. if (old.v32 == new.v32)
  247. return;
  248. put_unaligned(new.v32, (u32*)(opt + offset));
  249. inet_proto_csum_replace4(&tcph->check, pkt->skb,
  250. old.v32, new.v32, false);
  251. break;
  252. default:
  253. WARN_ON_ONCE(1);
  254. break;
  255. }
  256. return;
  257. }
  258. }
  259. static const struct nla_policy nft_exthdr_policy[NFTA_EXTHDR_MAX + 1] = {
  260. [NFTA_EXTHDR_DREG] = { .type = NLA_U32 },
  261. [NFTA_EXTHDR_TYPE] = { .type = NLA_U8 },
  262. [NFTA_EXTHDR_OFFSET] = { .type = NLA_U32 },
  263. [NFTA_EXTHDR_LEN] = { .type = NLA_U32 },
  264. [NFTA_EXTHDR_FLAGS] = { .type = NLA_U32 },
  265. [NFTA_EXTHDR_OP] = { .type = NLA_U32 },
  266. [NFTA_EXTHDR_SREG] = { .type = NLA_U32 },
  267. };
  268. static int nft_exthdr_init(const struct nft_ctx *ctx,
  269. const struct nft_expr *expr,
  270. const struct nlattr * const tb[])
  271. {
  272. struct nft_exthdr *priv = nft_expr_priv(expr);
  273. u32 offset, len, flags = 0, op = NFT_EXTHDR_OP_IPV6;
  274. int err;
  275. if (!tb[NFTA_EXTHDR_DREG] ||
  276. !tb[NFTA_EXTHDR_TYPE] ||
  277. !tb[NFTA_EXTHDR_OFFSET] ||
  278. !tb[NFTA_EXTHDR_LEN])
  279. return -EINVAL;
  280. err = nft_parse_u32_check(tb[NFTA_EXTHDR_OFFSET], U8_MAX, &offset);
  281. if (err < 0)
  282. return err;
  283. err = nft_parse_u32_check(tb[NFTA_EXTHDR_LEN], U8_MAX, &len);
  284. if (err < 0)
  285. return err;
  286. if (tb[NFTA_EXTHDR_FLAGS]) {
  287. err = nft_parse_u32_check(tb[NFTA_EXTHDR_FLAGS], U8_MAX, &flags);
  288. if (err < 0)
  289. return err;
  290. if (flags & ~NFT_EXTHDR_F_PRESENT)
  291. return -EINVAL;
  292. }
  293. if (tb[NFTA_EXTHDR_OP]) {
  294. err = nft_parse_u32_check(tb[NFTA_EXTHDR_OP], U8_MAX, &op);
  295. if (err < 0)
  296. return err;
  297. }
  298. priv->type = nla_get_u8(tb[NFTA_EXTHDR_TYPE]);
  299. priv->offset = offset;
  300. priv->len = len;
  301. priv->dreg = nft_parse_register(tb[NFTA_EXTHDR_DREG]);
  302. priv->flags = flags;
  303. priv->op = op;
  304. return nft_validate_register_store(ctx, priv->dreg, NULL,
  305. NFT_DATA_VALUE, priv->len);
  306. }
  307. static int nft_exthdr_tcp_set_init(const struct nft_ctx *ctx,
  308. const struct nft_expr *expr,
  309. const struct nlattr * const tb[])
  310. {
  311. struct nft_exthdr *priv = nft_expr_priv(expr);
  312. u32 offset, len, flags = 0, op = NFT_EXTHDR_OP_IPV6;
  313. int err;
  314. if (!tb[NFTA_EXTHDR_SREG] ||
  315. !tb[NFTA_EXTHDR_TYPE] ||
  316. !tb[NFTA_EXTHDR_OFFSET] ||
  317. !tb[NFTA_EXTHDR_LEN])
  318. return -EINVAL;
  319. if (tb[NFTA_EXTHDR_DREG] || tb[NFTA_EXTHDR_FLAGS])
  320. return -EINVAL;
  321. err = nft_parse_u32_check(tb[NFTA_EXTHDR_OFFSET], U8_MAX, &offset);
  322. if (err < 0)
  323. return err;
  324. err = nft_parse_u32_check(tb[NFTA_EXTHDR_LEN], U8_MAX, &len);
  325. if (err < 0)
  326. return err;
  327. if (offset < 2)
  328. return -EOPNOTSUPP;
  329. switch (len) {
  330. case 2: break;
  331. case 4: break;
  332. default:
  333. return -EOPNOTSUPP;
  334. }
  335. err = nft_parse_u32_check(tb[NFTA_EXTHDR_OP], U8_MAX, &op);
  336. if (err < 0)
  337. return err;
  338. priv->type = nla_get_u8(tb[NFTA_EXTHDR_TYPE]);
  339. priv->offset = offset;
  340. priv->len = len;
  341. priv->sreg = nft_parse_register(tb[NFTA_EXTHDR_SREG]);
  342. priv->flags = flags;
  343. priv->op = op;
  344. return nft_validate_register_load(priv->sreg, priv->len);
  345. }
  346. static int nft_exthdr_ipv4_init(const struct nft_ctx *ctx,
  347. const struct nft_expr *expr,
  348. const struct nlattr * const tb[])
  349. {
  350. struct nft_exthdr *priv = nft_expr_priv(expr);
  351. int err = nft_exthdr_init(ctx, expr, tb);
  352. if (err < 0)
  353. return err;
  354. switch (priv->type) {
  355. case IPOPT_SSRR:
  356. case IPOPT_LSRR:
  357. case IPOPT_RR:
  358. case IPOPT_RA:
  359. break;
  360. default:
  361. return -EOPNOTSUPP;
  362. }
  363. return 0;
  364. }
  365. static int nft_exthdr_dump_common(struct sk_buff *skb, const struct nft_exthdr *priv)
  366. {
  367. if (nla_put_u8(skb, NFTA_EXTHDR_TYPE, priv->type))
  368. goto nla_put_failure;
  369. if (nla_put_be32(skb, NFTA_EXTHDR_OFFSET, htonl(priv->offset)))
  370. goto nla_put_failure;
  371. if (nla_put_be32(skb, NFTA_EXTHDR_LEN, htonl(priv->len)))
  372. goto nla_put_failure;
  373. if (nla_put_be32(skb, NFTA_EXTHDR_FLAGS, htonl(priv->flags)))
  374. goto nla_put_failure;
  375. if (nla_put_be32(skb, NFTA_EXTHDR_OP, htonl(priv->op)))
  376. goto nla_put_failure;
  377. return 0;
  378. nla_put_failure:
  379. return -1;
  380. }
  381. static int nft_exthdr_dump(struct sk_buff *skb, const struct nft_expr *expr)
  382. {
  383. const struct nft_exthdr *priv = nft_expr_priv(expr);
  384. if (nft_dump_register(skb, NFTA_EXTHDR_DREG, priv->dreg))
  385. return -1;
  386. return nft_exthdr_dump_common(skb, priv);
  387. }
  388. static int nft_exthdr_dump_set(struct sk_buff *skb, const struct nft_expr *expr)
  389. {
  390. const struct nft_exthdr *priv = nft_expr_priv(expr);
  391. if (nft_dump_register(skb, NFTA_EXTHDR_SREG, priv->sreg))
  392. return -1;
  393. return nft_exthdr_dump_common(skb, priv);
  394. }
  395. static const struct nft_expr_ops nft_exthdr_ipv6_ops = {
  396. .type = &nft_exthdr_type,
  397. .size = NFT_EXPR_SIZE(sizeof(struct nft_exthdr)),
  398. .eval = nft_exthdr_ipv6_eval,
  399. .init = nft_exthdr_init,
  400. .dump = nft_exthdr_dump,
  401. };
  402. static const struct nft_expr_ops nft_exthdr_ipv4_ops = {
  403. .type = &nft_exthdr_type,
  404. .size = NFT_EXPR_SIZE(sizeof(struct nft_exthdr)),
  405. .eval = nft_exthdr_ipv4_eval,
  406. .init = nft_exthdr_ipv4_init,
  407. .dump = nft_exthdr_dump,
  408. };
  409. static const struct nft_expr_ops nft_exthdr_tcp_ops = {
  410. .type = &nft_exthdr_type,
  411. .size = NFT_EXPR_SIZE(sizeof(struct nft_exthdr)),
  412. .eval = nft_exthdr_tcp_eval,
  413. .init = nft_exthdr_init,
  414. .dump = nft_exthdr_dump,
  415. };
  416. static const struct nft_expr_ops nft_exthdr_tcp_set_ops = {
  417. .type = &nft_exthdr_type,
  418. .size = NFT_EXPR_SIZE(sizeof(struct nft_exthdr)),
  419. .eval = nft_exthdr_tcp_set_eval,
  420. .init = nft_exthdr_tcp_set_init,
  421. .dump = nft_exthdr_dump_set,
  422. };
  423. static const struct nft_expr_ops *
  424. nft_exthdr_select_ops(const struct nft_ctx *ctx,
  425. const struct nlattr * const tb[])
  426. {
  427. u32 op;
  428. if (!tb[NFTA_EXTHDR_OP])
  429. return &nft_exthdr_ipv6_ops;
  430. if (tb[NFTA_EXTHDR_SREG] && tb[NFTA_EXTHDR_DREG])
  431. return ERR_PTR(-EOPNOTSUPP);
  432. op = ntohl(nla_get_be32(tb[NFTA_EXTHDR_OP]));
  433. switch (op) {
  434. case NFT_EXTHDR_OP_TCPOPT:
  435. if (tb[NFTA_EXTHDR_SREG])
  436. return &nft_exthdr_tcp_set_ops;
  437. if (tb[NFTA_EXTHDR_DREG])
  438. return &nft_exthdr_tcp_ops;
  439. break;
  440. case NFT_EXTHDR_OP_IPV6:
  441. if (tb[NFTA_EXTHDR_DREG])
  442. return &nft_exthdr_ipv6_ops;
  443. break;
  444. case NFT_EXTHDR_OP_IPV4:
  445. if (ctx->family != NFPROTO_IPV6) {
  446. if (tb[NFTA_EXTHDR_DREG])
  447. return &nft_exthdr_ipv4_ops;
  448. }
  449. break;
  450. }
  451. return ERR_PTR(-EOPNOTSUPP);
  452. }
  453. struct nft_expr_type nft_exthdr_type __read_mostly = {
  454. .name = "exthdr",
  455. .select_ops = nft_exthdr_select_ops,
  456. .policy = nft_exthdr_policy,
  457. .maxattr = NFTA_EXTHDR_MAX,
  458. .owner = THIS_MODULE,
  459. };