1/* Kernel module to match FRAG parameters. */ 2 3/* (C) 2001-2002 Andras Kis-Szabo <kisza@sch.bme.hu> 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License version 2 as 7 * published by the Free Software Foundation. 8 */ 9#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 10#include <linux/module.h> 11#include <linux/skbuff.h> 12#include <linux/ipv6.h> 13#include <linux/types.h> 14#include <net/checksum.h> 15#include <net/ipv6.h> 16 17#include <linux/netfilter/x_tables.h> 18#include <linux/netfilter_ipv6/ip6_tables.h> 19#include <linux/netfilter_ipv6/ip6t_frag.h> 20 21MODULE_LICENSE("GPL"); 22MODULE_DESCRIPTION("Xtables: IPv6 fragment match"); 23MODULE_AUTHOR("Andras Kis-Szabo <kisza@sch.bme.hu>"); 24 25/* Returns 1 if the id is matched by the range, 0 otherwise */ 26static inline bool 27id_match(u_int32_t min, u_int32_t max, u_int32_t id, bool invert) 28{ 29 bool r; 30 pr_debug("id_match:%c 0x%x <= 0x%x <= 0x%x\n", invert ? '!' : ' ', 31 min, id, max); 32 r = (id >= min && id <= max) ^ invert; 33 pr_debug(" result %s\n", r ? "PASS" : "FAILED"); 34 return r; 35} 36 37static bool 38frag_mt6(const struct sk_buff *skb, struct xt_action_param *par) 39{ 40 struct frag_hdr _frag; 41 const struct frag_hdr *fh; 42 const struct ip6t_frag *fraginfo = par->matchinfo; 43 unsigned int ptr = 0; 44 int err; 45 46 err = ipv6_find_hdr(skb, &ptr, NEXTHDR_FRAGMENT, NULL, NULL); 47 if (err < 0) { 48 if (err != -ENOENT) 49 par->hotdrop = true; 50 return false; 51 } 52 53 fh = skb_header_pointer(skb, ptr, sizeof(_frag), &_frag); 54 if (fh == NULL) { 55 par->hotdrop = true; 56 return false; 57 } 58 59 pr_debug("INFO %04X ", fh->frag_off); 60 pr_debug("OFFSET %04X ", ntohs(fh->frag_off) & ~0x7); 61 pr_debug("RES %02X %04X", fh->reserved, ntohs(fh->frag_off) & 0x6); 62 pr_debug("MF %04X ", fh->frag_off & htons(IP6_MF)); 63 pr_debug("ID %u %08X\n", ntohl(fh->identification), 64 ntohl(fh->identification)); 65 66 pr_debug("IPv6 FRAG id %02X ", 67 id_match(fraginfo->ids[0], fraginfo->ids[1], 68 ntohl(fh->identification), 69 !!(fraginfo->invflags & IP6T_FRAG_INV_IDS))); 70 pr_debug("res %02X %02X%04X %02X ", 71 fraginfo->flags & IP6T_FRAG_RES, fh->reserved, 72 ntohs(fh->frag_off) & 0x6, 73 !((fraginfo->flags & IP6T_FRAG_RES) && 74 (fh->reserved || (ntohs(fh->frag_off) & 0x06)))); 75 pr_debug("first %02X %02X %02X ", 76 fraginfo->flags & IP6T_FRAG_FST, 77 ntohs(fh->frag_off) & ~0x7, 78 !((fraginfo->flags & IP6T_FRAG_FST) && 79 (ntohs(fh->frag_off) & ~0x7))); 80 pr_debug("mf %02X %02X %02X ", 81 fraginfo->flags & IP6T_FRAG_MF, 82 ntohs(fh->frag_off) & IP6_MF, 83 !((fraginfo->flags & IP6T_FRAG_MF) && 84 !((ntohs(fh->frag_off) & IP6_MF)))); 85 pr_debug("last %02X %02X %02X\n", 86 fraginfo->flags & IP6T_FRAG_NMF, 87 ntohs(fh->frag_off) & IP6_MF, 88 !((fraginfo->flags & IP6T_FRAG_NMF) && 89 (ntohs(fh->frag_off) & IP6_MF))); 90 91 return (fh != NULL) && 92 id_match(fraginfo->ids[0], fraginfo->ids[1], 93 ntohl(fh->identification), 94 !!(fraginfo->invflags & IP6T_FRAG_INV_IDS)) && 95 !((fraginfo->flags & IP6T_FRAG_RES) && 96 (fh->reserved || (ntohs(fh->frag_off) & 0x6))) && 97 !((fraginfo->flags & IP6T_FRAG_FST) && 98 (ntohs(fh->frag_off) & ~0x7)) && 99 !((fraginfo->flags & IP6T_FRAG_MF) && 100 !(ntohs(fh->frag_off) & IP6_MF)) && 101 !((fraginfo->flags & IP6T_FRAG_NMF) && 102 (ntohs(fh->frag_off) & IP6_MF)); 103} 104 105static int frag_mt6_check(const struct xt_mtchk_param *par) 106{ 107 const struct ip6t_frag *fraginfo = par->matchinfo; 108 109 if (fraginfo->invflags & ~IP6T_FRAG_INV_MASK) { 110 pr_debug("unknown flags %X\n", fraginfo->invflags); 111 return -EINVAL; 112 } 113 return 0; 114} 115 116static struct xt_match frag_mt6_reg __read_mostly = { 117 .name = "frag", 118 .family = NFPROTO_IPV6, 119 .match = frag_mt6, 120 .matchsize = sizeof(struct ip6t_frag), 121 .checkentry = frag_mt6_check, 122 .me = THIS_MODULE, 123}; 124 125static int __init frag_mt6_init(void) 126{ 127 return xt_register_match(&frag_mt6_reg); 128} 129 130static void __exit frag_mt6_exit(void) 131{ 132 xt_unregister_match(&frag_mt6_reg); 133} 134 135module_init(frag_mt6_init); 136module_exit(frag_mt6_exit); 137