Writing An Ebtables Match Module Tutorial

One of the biggest problems I have had when working with Linux and other open-source projects is the lack of up-to-date documentation. Some projects go to great lengths to modernize documentation and others don't at all which results in the project being forgotten. Netfilter Xtables modules while in use on almost every system, seem to not get the polish they deserve for tutorials, examples and updated documentation. After all - Netfilter makes Linux so attractive for network appliances and where would we be without the work of Pablo, Patrick, Florian, Harold, Eric and others (sorry if I forgot anyone, but hi!).

I recently had to write a module to extend the Ebtables 802.3 module which was not providing enough granularity to filter LLC frames with differing DSAP and SSAP values. It is common for the two to differ, however, the developer(s) responsible thought that it shouldn't. This document includes the source code for my module and how to compile it.

Before continuing though, you should be aware that not only is there a kernel component, but there is also a user space component. This is the same process (almost) for ebtables, iptables and ip6tables.

Getting Started

Before getting started, you will need to have a system with all of the pre-requisites required to build the kernel for your distribution. Typically this involves the build-essentials meta package etc... Here are few posts that will help your get started:

Next, you will need a copy of the kernel - for the purposes of this tutorial, I will use 3.12.20. You can get it from Kernel.org.

Decompress the kernel using the following command:

tar xf [linux archive]

Writing The Ebtables Kernel Module

When writing a kernel module, you must do the following to build your module and to have it listed in the menuconfig. At a minimum you will need to:

  • Modify the Makefile associated for the modules in this component (ebtables Makefile is under net/bridge/netfilter/
  • Modify the Kconfig or kernel config to include the module (again under correct component)
  • Source for your module

Lets start by modifying the Makefile (net/bridge/netfilter/Makefile) and lets add the module called

Below this section, adjust to include the new 802_3 module

  1. #matches
  2. obj-$(CONFIG_BRIDGE_EBT_802_3) += ebt_802_3.o
  3. obj-$(CONFIG_BRIDGE_EBT_AMONG) += ebt_among.o
  1. #matches
  2. obj-$(CONFIG_BRIDGE_EBT_802_3) += ebt_802_3.o
  3. obj-$(CONFIG_BRIDGE_EBT_802_3_NG) += ebt_802_3_ng.o
  4. obj-$(CONFIG_BRIDGE_EBT_AMONG) += ebt_among.o

Now in the Kconfig file (net/bridge/netfilter/Kconfig) below the following section, add in a completely new section for the new 802_3_ng module.

  1. #
  2. # matches
  3. #
  4. config BRIDGE_EBT_802_3
  5.         tristate "ebt: 802.3 filter support"
  6.         help
  7.           This option adds matching support for 802.3 Ethernet frames.
  8.  
  9.           To compile it as a module, choose M here.  If unsure, say N.
  1. config BRIDGE_EBT_802_3_NG
  2.         tristate "ebt: 802.3 extended filter support"
  3.         help
  4.           This option adds extending matching support for 802.3 Ethernet frames -
  5.           allows different SAP values to be matched - ie. IBM SNA
  6.  
  7.           To compile it as a module, choose M here.  If unsure, say N.

The above two steps should be fairly self explanatory, however, the first adds the module to the list of objects to be compiled should it be enabled in the menuconfig which is created by the second step (above).

Now create the modules source file (net/bridge/netfilter/ebt_802_3_ng.c)

  1. /**
  2.  * @file ebt_802_3_ng.c
  3.  * @author Ron Brash - [email protected]
  4.  * @date May 2014
  5.  * @brief Extend ebtables match module for cases where LLC traffic has
  6.  * differing SSAP and DSAP values - ie. IBM SNA
  7.  */
  8. #include <linux/module.h>
  9. #include <linux/netfilter/x_tables.h>
  10. #include <linux/netfilter_bridge/ebtables.h>
  11. #include <linux/netfilter_bridge/ebt_802_3.h>
  12.  
  13. #define EBT_802_3_NG_DSAP 0x01
  14. #define EBT_802_3_NG_SSAP 0x02
  15.  
  16. #define EBT_802_3_NG_MASK (EBT_802_3_NG_DSAP | EBT_802_3_NG_SSAP | EBT_802_3)
  17.  
  18. struct ebt_802_3_ng_info {
  19.         __u8 dsap;
  20.         __u8 ssap;
  21.         __u8 bitmask;
  22.         __u8 invflags;
  23. };
  24.  
  25. static bool ebt_802_3_ng_mt(const struct sk_buff *skb, struct xt_action_param *par)
  26. {
  27.         const struct ebt_802_3_ng_info *info = par->matchinfo;
  28.         const struct ebt_802_3_hdr *hdr = ebt_802_3_hdr(skb);
  29.  
  30.         if (info->bitmask & EBT_802_3_NG_DSAP) {
  31.                 if (FWINV(info->dsap != hdr->llc.ui.dsap, EBT_802_3_NG_DSAP))
  32.                         return false;
  33.         }
  34.  
  35.         if (info->bitmask & EBT_802_3_NG_SSAP) {
  36.                 if (FWINV(info->ssap != hdr->llc.ui.ssap, EBT_802_3_NG_SSAP))
  37.                         return false;
  38.         }
  39.  
  40.         return true;
  41. }
  42.  
  43. static int ebt_802_3_ng_mt_check(const struct xt_mtchk_param *par)
  44. {
  45.         const struct ebt_802_3_ng_info *info = par->matchinfo;
  46.  
  47.         if (info->bitmask & ~EBT_802_3_NG_MASK || info->invflags & ~EBT_802_3_NG_MASK)
  48.                 return -EINVAL;
  49.  
  50.         return 0;
  51. }
  52.  
  53. static struct xt_match ebt_802_3_ng_mt_reg __read_mostly = {
  54.         .name = "802_3_ng",
  55.         .revision = 0,
  56.         .family = NFPROTO_BRIDGE,
  57.         .match = ebt_802_3_ng_mt,
  58.         .checkentry = ebt_802_3_ng_mt_check,
  59.         .matchsize = sizeof(struct ebt_802_3_ng_info),
  60.         .me = THIS_MODULE,
  61. };
  62.  
  63. static int __init ebt_802_3_ng_init(void)
  64. {
  65.         return xt_register_match(&ebt_802_3_ng_mt_reg);
  66. }
  67.  
  68. static void __exit ebt_802_3_ng_fini(void)
  69. {
  70.         xt_unregister_match(&ebt_802_3_ng_mt_reg);
  71. }
  72.  
  73. module_init(ebt_802_3_ng_init);
  74. module_exit(ebt_802_3_ng_fini);
  75. MODULE_DESCRIPTION("Ebtables DSAP-SSAP fields matching");
  76. MODULE_LICENSE("GPL");

Once the above is created, you may now run make menuconfig and enable the new module. It will be located under: Networking Support > Networking Options > Network packet filtering framework (Netfilter)

Writing The Ebtables User Space Extension

Now to activate and give your new module parameters, a user space application has to be executed - for the module above, we will be executing ebtables in user space. For the purposes of this, I used the patches and source available for Ebtables from here.

Untar the source and apply the patches (if you want). Next, navigate into the source directory and then into the extensions. Open the Makefile (extensions/Makefile) and edit the following to look like the below:

  1. #! /usr/bin/make
  2.  
  3. EXT_FUNC+=802_3 nat arp arpreply ip ip6 standard log redirect vlan mark_m mark \
  4.           pkttype stp among limit ulog nflog
  5. EXT_TABLES+=filter nat broute
  6. <code>
  7.  
  8. <code>
  9. #! /usr/bin/make
  10.  
  11. EXT_FUNC+=802_3 802_3_ng nat arp arpreply ip ip6 standard log redirect vlan mark_m mark \
  12.           pkttype stp among limit ulog nflog
  13. EXT_TABLES+=filter nat broute

Now create the source file for the new 802_3 module (extensions/ebt_802_3_ng.c)

  1. /**
  2.  * @file ebt_802_3_ng.c
  3.  * @author Ron Brash - [email protected]
  4.  * @date May 2014
  5.  * @brief Extend ebtables match module for cases where LLC traffic has
  6.  * differing SSAP and DSAP values - ie. IBM SNA
  7. + * @note IE ebtables -A FORWARD -p LENGTH --802_3_ng-dsap 0x0c --802_3_ng-ssap 0xfc -j ACCEPT
  8.  */
  9.  
  10. #include <stdio.h>
  11. #include <string.h>
  12. #include <stdlib.h>
  13. #include <getopt.h>
  14. #include "../include/ebtables_u.h"
  15. #include "../include/ethernetdb.h"
  16. #include <linux/netfilter_bridge/ebt_802_3.h>
  17.  
  18. #define _802_3_NG_DSAP 1
  19. #define _802_3_NG_SSAP 2
  20.  
  21. #define EBT_802_3_NG_DSAP 0x01
  22. #define EBT_802_3_NG_SSAP 0x02
  23.  
  24. #define EBT_802_3_NG_MASK (EBT_802_3_NG_DSAP | EBT_802_3_NG_SSAP | EBT_802_3)
  25.  
  26. struct ebt_802_3_info_ng {
  27.         __u8 dsap;
  28.         __u8 ssap;
  29.         __u8 bitmask;
  30.         __u8 invflags;
  31. };
  32.  
  33. static struct option opts[] = {
  34.         {"802_3_ng-dsap", required_argument, 0, _802_3_NG_DSAP},
  35.         {"802_3_ng-ssap", required_argument, 0, _802_3_NG_SSAP},
  36.         {0}
  37. };
  38.  
  39. static void print_help()
  40. {
  41.         printf("802_3_ng options:\n"
  42.                "--802_3_ng-ssap [!] protocol       : 802.3 SSAP - 1 byte value (hex)\n"
  43.                "--802_3_ng-dsap [!] protocol       : 802.3 DSAP - 1 byte value (hex)\n");
  44. }
  45.  
  46. static void init(struct ebt_entry_match *match)
  47. {
  48.         struct ebt_802_3_info_ng *info = (struct ebt_802_3_info_ng *)match->data;
  49.  
  50.         info->dsap = 0;
  51.         info->ssap = 0;
  52.         info->invflags = 0;
  53.         info->bitmask = 0;
  54. }
  55.  
  56. static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry,
  57.                  unsigned int *flags, struct ebt_entry_match **match)
  58. {
  59.         struct ebt_802_3_info_ng *info = (struct ebt_802_3_info_ng *)(*match)->data;
  60.         unsigned int i;
  61.         char *end;
  62.  
  63.         switch (c) {
  64.         case _802_3_NG_DSAP:
  65.                 ebt_check_option2(flags, _802_3_NG_DSAP);
  66.                 if (ebt_check_inverse2(optarg))
  67.                         info->invflags |= EBT_802_3_NG_DSAP;
  68.                 i = strtoul(optarg, &end, 16);
  69.                 if (i > 255 || *end != '\0')
  70.                         ebt_print_error2("Problem with specified " "DSAP hex value, %x", i);
  71.                 info->dsap = i; /* one byte, so no byte order worries */
  72.                 info->bitmask |= EBT_802_3_NG_DSAP;
  73.                 break;
  74.         case _802_3_NG_SSAP:
  75.                 ebt_check_option2(flags, _802_3_NG_SSAP);
  76.                 if (ebt_check_inverse2(optarg))
  77.                         info->invflags |= EBT_802_3_NG_SSAP;
  78.                 i = strtoul(optarg, &end, 16);
  79.                 if (i > 255 || *end != '\0')
  80.                         ebt_print_error2("Problem with specified " "SSAP hex value, %x", i);
  81.                 info->ssap = i; /* one byte, so no byte order worries */
  82.                 info->bitmask |= EBT_802_3_NG_SSAP;
  83.                 break;
  84.         default:
  85.  
  86.                 return 0;
  87.         }
  88.  
  89.         return 1;
  90. }
  91.  
  92. static void final_check(const struct ebt_u_entry *entry,
  93.                         const struct ebt_entry_match *match, const char *name, unsigned int hookmask, unsigned int time)
  94. {
  95.         if (!(entry->bitmask & EBT_802_3))
  96.                 ebt_print_error("For 802.3 DSAP/SSAP filtering the protocol " "must be LENGTH");
  97. }
  98.  
  99. static void print(const struct ebt_u_entry *entry, const struct ebt_entry_match *match)
  100. {
  101.         struct ebt_802_3_info_ng *info = (struct ebt_802_3_info_ng *)match->data;
  102.  
  103.         if (info->bitmask & EBT_802_3_NG_SSAP) {
  104.                 printf("--802_3_ng-ssap ");
  105.                 if (info->invflags & EBT_802_3_NG_SSAP)
  106.                         printf("! ");
  107.                 printf("0x%.2x ", info->ssap);
  108.         }
  109.         if (info->bitmask & EBT_802_3_NG_DSAP) {
  110.                 printf("--802_3_ng-dsap ");
  111.                 if (info->invflags & EBT_802_3_NG_DSAP)
  112.                         printf("! ");
  113.                 printf("0x%.2x ", info->dsap);
  114.         }
  115. }
  116.  
  117. static int compare(const struct ebt_entry_match *m1, const struct ebt_entry_match *m2)
  118. {
  119.         struct ebt_802_3_info_ng *info1 = (struct ebt_802_3_info_ng *)m1->data;
  120.         struct ebt_802_3_info_ng *info2 = (struct ebt_802_3_info_ng *)m2->data;
  121.  
  122.         if (memcmp(info1, info2, sizeof(struct ebt_802_3_info_ng)) == 0)
  123.                 return 1;
  124.         else
  125.                 return 0;
  126. }
  127.  
  128. static struct ebt_u_match _802_3_ng_match = {
  129.         .name = "802_3_ng",
  130.         .size = sizeof(struct ebt_802_3_info_ng),
  131.         .help = print_help,
  132.         .init = init,
  133.         .parse = parse,
  134.         .final_check = final_check,
  135.         .print = print,
  136.         .compare = compare,
  137.         .extra_ops = opts,
  138. };
  139.  
  140. void _init(void)
  141. {
  142.         ebt_register_match(&_802_3_ng_match);
  143. }

Now build and install your modified ebtables. Assuming you have a bridge enabled, you can euse/test your new code with a command such as this:

ebtables -A FORWARD -p LENGTH --802_3_ng-dsap 0x0c --802_3_ng-ssap 0xfc -j ACCEPT

Note that the type is LENGTH because it is a non-IP protocol!

I hope this helps explain ebtables and the process to make a module!

Blog tags: 

AttachmentSize
Kernel config116.97 KB

Add new comment

Filtered HTML

  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <blockquote> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • Lines and paragraphs break automatically.

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
By submitting this form, you accept the Mollom privacy policy.