This shows you the differences between two versions of the page.
isc:labs:kernel:tasks:03 [2024/10/19 19:01] florin.stancu removed |
— (current) | ||
---|---|---|---|
Line 1: | Line 1: | ||
- | === 03. [??p] Extending the Linux firewall === | ||
- | <note tip> | ||
- | //Pro tip//: since you may want to consult the **man** pages at some point, add this to your //.bashrc// or //.zshrc//: | ||
- | <code bash> | ||
- | # color schemes for man pages | ||
- | man() { | ||
- | LESS_TERMCAP_mb=$'\e[1;34m' \ | ||
- | LESS_TERMCAP_md=$'\e[1;32m' \ | ||
- | LESS_TERMCAP_so=$'\e[1;33m' \ | ||
- | LESS_TERMCAP_us=$'\e[1;4;31m' \ | ||
- | LESS_TERMCAP_me=$'\e[0m' \ | ||
- | LESS_TERMCAP_se=$'\e[0m' \ | ||
- | LESS_TERMCAP_ue=$'\e[0m' \ | ||
- | command man "$@" | ||
- | } | ||
- | </code> | ||
- | |||
- | Now, source your file to load the new command. **man** will color certain keywords appropriately. | ||
- | |||
- | <code bash> | ||
- | # update your shell's environment with the man() wrapper | ||
- | [student@host]$ source ~/.bashrc | ||
- | |||
- | # check out the manual page for iptables | ||
- | [student@host]$ man iptables | ||
- | </code> | ||
- | </note> | ||
- | |||
- | **iptables** is a configuration tool for the kernel packet filter. | ||
- | |||
- | The system as a whole provides many functionalities that are grouped by **tables**: //filter, nat, mangle, raw, security//. If you want to alter a packet header, you place a rule in the //mangle// table. If you want to mask the private IP address of an internal host with the external IP address of the default gateway, you place a rule in the //nat// table. Depending on the table you choose, you will gain or lose access to some chains. If not specified, the default is the //filter// table. | ||
- | |||
- | **Chains** are basically lists of rules. The five built-in chains are //PREROUTING, FORWARD, POSTROUTING, INPUT, OUTPUT//. Each of these corresponds to certain locations in the network stack where packets trigger **Netfilter hooks** ([[https://elixir.bootlin.com/linux/latest/source/net/ipv4/ip_input.c#L540|here]] is the //PREROUTING// kernel hook as an example -- not that hard to add one, right?) For a selected chain, the order in which the rules are evaluated is determined primarily by the priority of their tables and secondarily by the user's discretionary arrangement (i.e.: order in which rules are inserted). | ||
- | |||
- | {{ :isc:labs:kernel:tasks:iptables_path.png?800 |}} | ||
- | |||
- | A **rule** consists of two entities: a sequence of match criteria and a jump target. | ||
- | |||
- | The **jump target** represents an action to be taken. You are most likely familiar with the built-in actions such as //ACCEPT// or //DROP//. These actions decide the ultimate fate of the packet and are final (i.e.: rule iteration stops when these are invoked). However, there are also extended actions (see ''man iptables-extensions(8)'') that are not terminal verdicts and can be used for various tasks such as auditing, forced checksum recalculation or removal of Explicit Congestion Notification (ECN) bits. | ||
- | |||
- | The **match criteria** of every rule are checked to determine if the jump target is applied. The way this is designed is very elegant: every type of feature (e.g.: layer 3 IP address vs layer 4 port) that you can check has a match callback function defined in the kernel. If you want, you can write your own such function in a Linux Kernel Module (LKM) and thus extend the functionality of **iptables** ([[https://inai.de/documents/Netfilter_Modules.pdf|Writing Netfilter Modules]] with code example). However, you will need to implement a userspace shared library counterpart. When you start an **iptables** process, it searches in ///usr/lib/xtables/ // and automatically loads certain shared libraries (note: this path can be overwritten or extended using the //XTABLES_LIBDIR// environment variable). Each library there must do three things: | ||
- | * define **iptables** flags for the new criteria that you want to include. | ||
- | * define help messages for when ''**iptables** %%--%%help'' is called (its help message is an amalgamation of each library's help snippet). | ||
- | * provide an initialization function for the structure containing the rule parameters; this structure will end up in the kernel's rule chain. | ||
- | So when you want to test the efficiency of the **iptables** rule evaluation process, keep in mind that each rule may imply the invocation of multiple callbacks such as [[https://elixir.bootlin.com/linux/latest/source/net/netfilter/xt_tcpudp.c#L66|this]]. | ||
- | |||
- | == [??p] Task A - Primer / Reminder == | ||
- | |||
- | Before writing our own match module, here's a small task to freshen your memory on how to use **iptables**. | ||
- | |||
- | Write an **iptables** rule according to the following specifications: | ||
- | * **chain:** OUTPUT | ||
- | * **match rule:** TCP packets originating from ephemeral ports bound to a socket created by //root// | ||
- | * **target:** enable kernel logging of matched packets with the //"TCP_LOG: "// prefix | ||
- | |||
- | How to test: | ||
- | <code bash> | ||
- | $ sudo curl www.google.com | ||
- | $ sudo dmesg | ||
- | </code> | ||
- | |||
- | <note tip> | ||
- | |||
- | **multiport**, **owner** modules | ||
- | |||
- | <code bash> | ||
- | $ man 8 iptables-extensions | ||
- | </code> | ||
- | </note> | ||
- | |||
- | <solution -hidden> | ||
- | <code bash> | ||
- | # "--log-prefix" must come after "-j LOG" | ||
- | $ sudo iptables \ | ||
- | -m multiport -m owner \ | ||
- | -I OUTPUT \ | ||
- | -p tcp \ | ||
- | --sports 1024:65535 \ | ||
- | --uid-owner root \ | ||
- | -j LOG \ | ||
- | --log-prefix 'TCP_LOG: ' | ||
- | </code> | ||
- | </solution> | ||
- | |||
- | == [??p] Task B - Writing an xtables module == | ||
- | |||
- | **xtables** is the backbone of **iptables** and provides a protocol-agnostic infrastructure for adding match modules. We touched on this topic earlier in the exercise but now we're going to jump right in. Following this brief introduction will be three subsections detailing the __//data structures//__, the __//user space shared library//__ and the __//kernel module//__. All these are partially implemented in the //%%02/%%// directory and you will have specific //TODOs// in the source files. By the end, you will have implemented a match filter for requested domains in DNS queries (i.e.: you can block DNS queries for //"google.com"// but not //"kernel.org"//, for example). Note that you can solve this task either in the VM, or on your localhost. We won't be doing anything dangerous, like overwriting kernel structures. | ||
- | |||
- | <note> | ||
- | If you're not that familiar with DNS, refer to [[https://courses.cs.duke.edu//fall16/compsci356/DNS/DNS-primer.pdf|this primer]] (Sec. 1-3). | ||
- | </note> | ||
- | |||
- | == The header == | ||
- | |||
- | We mentioned before that an **iptables** extension has two components. A kernel module implementing the verification of a rule and a user space library that is able to parse the user's rules (in **iptables** "syntax"). Considering that our module will be named **xt_dns_name** (the **xt_** part conforming to the naming convention), what links these two elements is the //%%include/xt_dns_name.h%%// header. In it, the **xt_dns_name_mtinfo** structure will hold all information necessary to the kernel module to match a packet. This structure will be initialized in user space and transferred over to the **xtables** framework by **iptables**. Looking closer, we notice two fields. While //name// will hold the queried domain name, //flags// specifies what features are enabled for verification. Our module is simple and has only one match criteria, but note that **iptables** can invert a selection by specifying the ''!'' symbol before it. So we can match a packet either on a queried domain name match, or a mismatch. | ||
- | |||
- | <spoiler> | ||
- | <file C xt_dns_name.h> | ||
- | #ifndef _XT_DNS_NAME_H | ||
- | #define _XT_DNS_NAME_H | ||
- | |||
- | /* defines enabled properties to be checked */ | ||
- | enum { | ||
- | XT_DNS_NAME = 1 << 0, /* search for DNS name match */ | ||
- | XT_DNS_NAME_INV = 1 << 1, /* search for DNS name mismatch */ | ||
- | }; | ||
- | |||
- | /* rule match information */ | ||
- | struct xt_dns_name_mtinfo { | ||
- | __u8 flags; | ||
- | __u8 name[127]; | ||
- | }; | ||
- | |||
- | #endif /* _XT_DNS_NAME_H */ | ||
- | </file> | ||
- | </spoiler> | ||
- | |||
- | <note> | ||
- | The **%%__u8%%** type is equivalent to **uint8_t** and is a typedef of **u8**. The kernel defines **u8**, **u16**, etc. because C types have [[https://static.lwn.net/images/pdf/LDD3/ch11.pdf|different sizes]], depending on the underlying CPU architecture. **%%__u8%%** is used here in stead of **u8** to indicate that the header is shared with userspace. Also, the reason why the kernel doesn't simply use **uint8_t** is because **u8** predates //stdint.h//. | ||
- | </note> | ||
- | |||
- | == The user space iptables plugin == | ||
- | |||
- | The plugin consists of a shared library compiled from //plugin/libxt_dns_name.c//. The source is broken down into four sections: | ||
- | * ''API'': These are prototypes of functions that must be made available to **iptables** to invoke when it needs certain things done. Here is a description of each function: | ||
- | * ''dns_name_mt_help()'': When you invoke ''iptables %%--%%help'', each plugin will print its own help message. This is our contribution. | ||
- | * ''dns_name_mt_init()'': Called before the argument parsing begins. Will zero out our **xt_dns_name_mtinfo** structure. | ||
- | * ''dns_name_mt_parse()'': Based on **optarg**, this function will be invoked for each argument that has something to do with our plugin. It will update the **xt_dns_name_mtinfo** structure accordingly on each pass. | ||
- | * ''dns_name_mt_check()'': Final check before sending the structure in kernel space. Will verify that all required arguments were provided. | ||
- | * ''dns_name_mt_print()'': When invoking ''iptables -L'', this function will print out a rule's match criteria. | ||
- | * ''dns_name_mt_save()'': Given a **xt_dns_name_mtinfo** structure, this function will print out the CLI arguments that would generate such a function. Called upon by **iptables-save**. | ||
- | * ''MODULE SPECIFICATION STRUCTURES'': Here we declare two structures: | ||
- | * ''dns_name_mt_opts'' : A structure defining the CLI arguments that we accept (see ''man 3 getopt'' for details). | ||
- | * ''dns_name_mt_reg'' : A structure containing function pointers for specific tasks that the module needs to accomplish. We initialize it with the function described in the ''API'' section. This kind of structure is usually called a virtual table (or vtable). | ||
- | * ''IPTABLES MODULE CALLBACKS'': this is where we actually implement the functions that we added to the vtable. Here, you will search for //TODOs//. | ||
- | * ''LIBRARY MANAGEMENT FUNCTIONS'': When loaded, each library (i.e.: a //.so// file) can have a number of constructors defined. These constructors are functions called upon by the loader once the library was mapped in virtual memory. Our constructor will invoke **xtables_register_match()** in order to register the vtable with **iptables**, letting it know that it has yet another plugin at its disposal. | ||
- | |||
- | <spoiler> | ||
- | <file C libxt_dns_name.c> | ||
- | #include <stdio.h> | ||
- | #include <stdint.h> | ||
- | #include <getopt.h> | ||
- | #include <string.h> | ||
- | #include <xtables.h> | ||
- | |||
- | #include "xt_dns_name.h" | ||
- | |||
- | /****************************************************************************** | ||
- | ************************************ API ************************************* | ||
- | ******************************************************************************/ | ||
- | |||
- | static void dns_name_mt_help(void); | ||
- | static void dns_name_mt_init(struct xt_entry_match *match); | ||
- | static int dns_name_mt_parse(int c, char **argv, int invert, | ||
- | unsigned int *flags, const void *entry, | ||
- | struct xt_entry_match **match); | ||
- | static void dns_name_mt_check(unsigned int flags); | ||
- | static void dns_name_mt_print(const void *entry, | ||
- | const struct xt_entry_match *match, int numeric); | ||
- | static void dns_name_mt_save(const void *entry, | ||
- | const struct xt_entry_match *match); | ||
- | |||
- | /****************************************************************************** | ||
- | *********************** MODULE SPECIFICATION STRUCTURES ********************** | ||
- | ******************************************************************************/ | ||
- | |||
- | /* module specific options */ | ||
- | const struct option dns_name_mt_opts[] = { | ||
- | { .name="domain", .has_arg=required_argument, .val='1' }, | ||
- | { NULL }, | ||
- | }; | ||
- | |||
- | /* module userspace extension vtable */ | ||
- | static struct xtables_match dns_name_mt_reg = { | ||
- | .version = XTABLES_VERSION, | ||
- | .name = "dns_name", | ||
- | .revision = 0, | ||
- | .family = NFPROTO_IPV4, | ||
- | .size = XT_ALIGN(sizeof(struct xt_dns_name_mtinfo)), | ||
- | .userspacesize = XT_ALIGN(sizeof(struct xt_dns_name_mtinfo)), | ||
- | .help = dns_name_mt_help, | ||
- | .init = dns_name_mt_init, | ||
- | .parse = dns_name_mt_parse, | ||
- | .final_check = dns_name_mt_check, | ||
- | .print = dns_name_mt_print, | ||
- | .save = dns_name_mt_save, | ||
- | .extra_opts = dns_name_mt_opts, | ||
- | }; | ||
- | |||
- | /****************************************************************************** | ||
- | ************************* IPTABLES MODULE CALLBACKS ************************** | ||
- | ******************************************************************************/ | ||
- | |||
- | /* dns_name_mt_help - prints help message for this module | ||
- | */ | ||
- | static void | ||
- | dns_name_mt_help(void) | ||
- | { | ||
- | printf("dns_name match options\n" | ||
- | "[!] --domain <string>\t\tQueried domain name.\n"); | ||
- | } | ||
- | |||
- | /* dns_name_mt_init - initializes our data struct fields before parsing | ||
- | * @match : pointer to our data struct | ||
- | */ | ||
- | static void | ||
- | dns_name_mt_init(struct xt_entry_match *match) | ||
- | { | ||
- | /* match is an internal structure; we are only interested in data */ | ||
- | struct xt_dns_name_mtinfo *info = (void *)match->data; | ||
- | |||
- | /* zero out structure */ | ||
- | memset(info, 0, sizeof(*info)); | ||
- | } | ||
- | |||
- | /* dns_name_mt_parse - called for each module-specific argument | ||
- | * @c : option id (see .val in dns_name_mt_opts) | ||
- | * @argv : argv (simple as that) | ||
- | * @invert : 1 if user specified "!" before argument | ||
- | * @flags : for parser's discretionary use | ||
- | * @entry : ptr to an ipt_entry struct (don't care) | ||
- | * @match : contains pointer to our data struct (data field) | ||
- | * | ||
- | * @return : true if option was parsed, false otherwise | ||
- | */ | ||
- | static int | ||
- | dns_name_mt_parse(int c, char **argv, int invert, unsigned int *flags, | ||
- | const void *entry, struct xt_entry_match **match) | ||
- | { | ||
- | /* get reference to our xt_dns_name_mtinfo instance */ | ||
- | struct xt_dns_name_mtinfo *info = (void *)(*match)->data; | ||
- | |||
- | /* option-specific parsing */ | ||
- | switch (c) { | ||
- | case '1': /* --domain */ | ||
- | /* check for multiple occurrences */ | ||
- | if (*flags & XT_DNS_NAME) | ||
- | xtables_error(PARAMETER_PROBLEM, "xt_dns_name: " | ||
- | "use \"--domain\" only once!"); | ||
- | |||
- | /* update parser flags and match criteria flags */ | ||
- | *flags |= XT_DNS_NAME; | ||
- | info->flags |= XT_DNS_NAME; | ||
- | |||
- | /* check for match rule inversion */ | ||
- | if (invert) | ||
- | info->flags |= XT_DNS_NAME_INV; | ||
- | |||
- | /* initalize info->name * | ||
- | * NOTE: argument is in global variable <optarg> * | ||
- | * NOTE: convert the "." characters according to QNAME specs */ | ||
- | |||
- | /* TODO 1: initialize info->name */ | ||
- | |||
- | return true; | ||
- | } | ||
- | |||
- | /* unknown option */ | ||
- | return false; | ||
- | } | ||
- | |||
- | /* dns_name_mt_check - verify that all required options were processed | ||
- | * @flags : the persistent <flags> argument from dns_name_mt_parse() | ||
- | */ | ||
- | static void | ||
- | dns_name_mt_check(unsigned int flags) | ||
- | { | ||
- | if (!(flags & XT_DNS_NAME)) | ||
- | xtables_error(PARAMETER_PROBLEM, "xt_dns_name: " | ||
- | "make sure to specify the \"--domain\" argument!"); | ||
- | } | ||
- | |||
- | /* dns_name_mt_print - print the match criteria fields for `iptables -L` | ||
- | * @entry : internal stuff (don't care) | ||
- | * @match : contains pointer to our data struct (data field) | ||
- | * @numeric : do not resolve IP addresses to host names if true (don't care) | ||
- | */ | ||
- | static void | ||
- | dns_name_mt_print(const void *entry, const struct xt_entry_match *match, | ||
- | int numeric) | ||
- | { | ||
- | const struct xt_dns_name_mtinfo *info = (void *) match->data; | ||
- | |||
- | /* check for match rule reversal */ | ||
- | if (info->flags & XT_DNS_NAME_INV) | ||
- | printf("! "); | ||
- | |||
- | /* print domain name * | ||
- | * NOTE: replace length of labels with "." characters * | ||
- | * NOTE: do NOT print a "\n" character */ | ||
- | |||
- | /* TODO 2: print info->name */ | ||
- | } | ||
- | |||
- | /* dns_name_mt_save - print out arguments that generate this rule | ||
- | * @entry : internal stuff (don't care) | ||
- | * @match : contains pointer to our data struct (data field) | ||
- | */ | ||
- | static void | ||
- | dns_name_mt_save(const void *entry, const struct xt_entry_match *match) | ||
- | { | ||
- | const struct xt_dns_name_mtinfo *info = (void *) match->data; | ||
- | |||
- | /* check for match rule reversal */ | ||
- | if (info->flags & XT_DNS_NAME_INV) | ||
- | printf("! "); | ||
- | |||
- | /* print "--domain" and the argument */ | ||
- | printf("--domain "); | ||
- | |||
- | /* TODO 3: copy paste TODO 2 here */ | ||
- | } | ||
- | |||
- | /****************************************************************************** | ||
- | ************************ LIBRARY MANAGEMENT FUNCTIONS ************************ | ||
- | ******************************************************************************/ | ||
- | |||
- | /* _init - iptables library constructor | ||
- | * | ||
- | * NOTE: the '_init' symbol is expanded as a macro by iptables | ||
- | */ | ||
- | static void | ||
- | _init(void) | ||
- | { | ||
- | xtables_register_match(&dns_name_mt_reg); | ||
- | } | ||
- | </file> | ||
- | </spoiler> | ||
- | |||
- | <note tip> | ||
- | When solving some of the //TODOs// and consulting the DNS format specification from before, it would be useful to have a DNS capture available to you in **wireshark**. | ||
- | </note> | ||
- | |||
- | == The kernel space xtables module == | ||
- | |||
- | The kernel module source is organized similarly to the user space plugin. The **dns_name_mt_reg** structure acts as a vtable but also includes information about permissible chains and layer 3 protocols that work with our implementation. Specifically, any rule that makes use of this module can be inserted only in the //OUTPUT// chain, meaning that we can only catch requests originating from our localhost. Moreover, we implement support only for IPv4, not for IPv6. As we can see, this structure is used on module initialization, in **dns_name_mt_init()**, to register our module with the **xtables** framework via **xt_register_match()**. | ||
- | |||
- | **dns_name_check()** and **dns_name_mt()** implement the functionalities required of our module. The former performs checks on each newly inserted rule, or at least on the part that pertains to this module. In other words, it must make sure that a valid domain name (i.e.: "."s replaced with length of following label, etc.) was inserted, for example. The latter function is called upon to verify if a packet matches a certain rule. Its first argument does not represent the packet itself, but a [[https://elixir.bootlin.com/linux/latest/source/include/linux/skbuff.h#L721|socket buffer]] structure (see also [[https://wiki.linuxfoundation.org/networking/sk_buff|this]], and possibly [[https://www.py4u.net/discuss/2125353|this]]) that contains this information, in addition to much, much more. We made sure to provide you with pointers to our **xt_dns_name_mtinfo** structure, but also to the beginning of the IPv4 header. However, it is up to you to implement this logic and obtain a working match module. | ||
- | |||
- | <spoiler> | ||
- | <file C xt_dns_name.c> | ||
- | #include <linux/kernel.h> | ||
- | #include <linux/netfilter/x_tables.h> | ||
- | #include <linux/skbuff.h> | ||
- | #include <linux/ip.h> | ||
- | #include <linux/module.h> | ||
- | |||
- | #include "xt_dns_name.h" | ||
- | |||
- | MODULE_DESCRIPTION("Xtables: DNS query QNAME matching"); | ||
- | MODULE_AUTHOR("Student"); | ||
- | MODULE_LICENSE("GPL"); | ||
- | MODULE_ALIAS("ipt_dns_name"); | ||
- | |||
- | #define MOD_TAG "xt_dns_name: " | ||
- | |||
- | /****************************************************************************** | ||
- | ************************************ API ************************************* | ||
- | ******************************************************************************/ | ||
- | |||
- | static int dns_name_check(const struct xt_mtchk_param *par); | ||
- | static bool dns_name_mt(const struct sk_buff *skb, struct xt_action_param *par); | ||
- | |||
- | /****************************************************************************** | ||
- | *********************** MODULE SPECIFICATION STRUCTURES ********************** | ||
- | ******************************************************************************/ | ||
- | |||
- | /* registration information */ | ||
- | static struct xt_match dns_name_mt_reg __read_mostly = { | ||
- | .name = "dns_name", | ||
- | .revision = 0, | ||
- | .family = NFPROTO_IPV4, | ||
- | .matchsize = sizeof(struct xt_dns_name_mtinfo), | ||
- | .checkentry = dns_name_check, | ||
- | .match = dns_name_mt, | ||
- | .hooks = 1 << NF_INET_LOCAL_OUT, | ||
- | .me = THIS_MODULE, | ||
- | }; | ||
- | |||
- | /****************************************************************************** | ||
- | ************************** XTABLES MODULE CALLBACKS ************************** | ||
- | ******************************************************************************/ | ||
- | |||
- | /* dns_name_check - checks rule validity | ||
- | * @par : parameters for match extensions | ||
- | * | ||
- | * @return : 0 if everything is ok, !0 otherwise | ||
- | */ | ||
- | static int | ||
- | dns_name_check(const struct xt_mtchk_param *par) | ||
- | { | ||
- | const struct xt_dns_name_mtinfo *info = par->matchinfo; | ||
- | |||
- | /* TODO 4: userspace is not to be trusted! check inserted rule */ | ||
- | |||
- | return 0; | ||
- | } | ||
- | |||
- | /* dns_name_mt - performs packet match check | ||
- | * @skb : packet buffer information | ||
- | * @par : parameters for matches / targets | ||
- | * | ||
- | * @return : true if matched, false otherwise | ||
- | */ | ||
- | static bool | ||
- | dns_name_mt(const struct sk_buff *skb, struct xt_action_param *par) | ||
- | { | ||
- | const struct xt_dns_name_mtinfo *info = par->matchinfo; | ||
- | struct iphdr *iph = ip_hdr(skb); | ||
- | |||
- | /* TODO 5: be 100% sure that the packet is a DNS request */ | ||
- | |||
- | /* TODO 6: match check on any & all QNAMEs in request */ | ||
- | |||
- | return false; | ||
- | } | ||
- | |||
- | |||
- | /****************************************************************************** | ||
- | *********************** MODULE ENTRY & EXIT CALLBACKS ************************ | ||
- | ******************************************************************************/ | ||
- | |||
- | static int dns_name_mt_init(void) | ||
- | { | ||
- | pr_info(MOD_TAG "loading xt_dns_name module"); | ||
- | return xt_register_match(&dns_name_mt_reg); | ||
- | } | ||
- | |||
- | static void dns_name_mt_exit(void) | ||
- | { | ||
- | pr_info(MOD_TAG "unloading xt_dns_name module"); | ||
- | xt_unregister_match(&dns_name_mt_reg); | ||
- | } | ||
- | |||
- | module_init(dns_name_mt_init); | ||
- | module_exit(dns_name_mt_exit); | ||
- | </file> | ||
- | </spoiler> | ||
- | |||
- | <note> | ||
- | Testing your solution: | ||
- | |||
- | <code bash> | ||
- | [student@host]$ sudo insmod xt_dns_name.ko | ||
- | |||
- | # depending on your distro, libxt_*.so may be installed in different places | ||
- | [student@host]$ sudo XTABLES_LIBDIR="$(pwd):/usr/lib/xtables:/usr/lib/x86_64-linux-gnu/xtables" \ | ||
- | iptables \ | ||
- | -m dns_name \ | ||
- | -I OUTPUT \ | ||
- | --domain 'fep.grid.pub.ro' \ | ||
- | -j DROP | ||
- | [student@host]$ dig +short fep.grid.pub.ro @8.8.8.8 | ||
- | |||
- | [student@host]$ sudo iptables -F OUTPUT | ||
- | [student@host]$ sudo rmmod xt_dns_name | ||
- | </code> | ||
- | |||
- | <note important> | ||
- | Try running a **wireshark** instance and filter by //"dns"// to make sure no queries pass through. \\ | ||
- | Note that some distros come with a DNS cache preinstalled and might make your filtering rule redundant. | ||
- | </note> | ||
- | |||
- | <note tip> | ||
- | Make sure to add plenty of **pr_info()** in your match function to make debugging easier. | ||
- | |||
- | ---- | ||
- | |||
- | Remember that although your host is most likely little-endian, the Internet is big-endian. So when accessing data that is larger than 1 byte (e.g.: port number), use the [[https://linux.die.net/man/3/htons|htons() family of functions]]. They should be readily available to you in the kernel module. | ||
- | </note> | ||
- | </note> |