1/* Shared library add-on to iptables to add addrtype matching support
2 *
3 * This program is released under the terms of GNU GPL */
4
5#include <stdio.h>
6#include <stdlib.h>
7#include <string.h>
8#include <getopt.h>
9#include <iptables.h>
10
11#include <linux/netfilter_ipv4/ip_tables.h>
12#include <linux/netfilter_ipv4/ipt_addrtype.h>
13
14/* from linux/rtnetlink.h, must match order of enumeration */
15static char *rtn_names[] = {
16	"UNSPEC",
17	"UNICAST",
18	"LOCAL",
19	"BROADCAST",
20	"ANYCAST",
21	"MULTICAST",
22	"BLACKHOLE",
23	"UNREACHABLE",
24	"PROHIBIT",
25	"THROW",
26	"NAT",
27	"XRESOLVE",
28	NULL
29};
30
31static void help_types(void)
32{
33	int i;
34
35	for (i = 0; rtn_names[i]; i++)
36		printf("                                %s\n", rtn_names[i]);
37}
38
39static void help(void)
40{
41	printf(
42"Address type match v%s options:\n"
43" [!] --src-type type[,...]      Match source address type\n"
44" [!] --dst-type type[,...]      Match destination address type\n"
45"\n"
46"Valid types:           \n"
47, IPTABLES_VERSION);
48	help_types();
49}
50
51static int
52parse_type(const char *name, size_t strlen, u_int16_t *mask)
53{
54	int i;
55
56	for (i = 0; rtn_names[i]; i++)
57		if (strncasecmp(name, rtn_names[i], strlen) == 0) {
58			/* build up bitmask for kernel module */
59			*mask |= (1 << i);
60			return 1;
61		}
62
63	return 0;
64}
65
66static void parse_types(const char *arg, u_int16_t *mask)
67{
68	const char *comma;
69
70	while ((comma = strchr(arg, ',')) != NULL) {
71		if (comma == arg || !parse_type(arg, comma-arg, mask))
72			exit_error(PARAMETER_PROBLEM,
73			           "addrtype: bad type `%s'", arg);
74		arg = comma + 1;
75	}
76
77	if (strlen(arg) == 0 || !parse_type(arg, strlen(arg), mask))
78		exit_error(PARAMETER_PROBLEM, "addrtype: bad type `%s'", arg);
79}
80
81#define IPT_ADDRTYPE_OPT_SRCTYPE	0x1
82#define IPT_ADDRTYPE_OPT_DSTTYPE	0x2
83
84static int parse(int c, char **argv, int invert, unsigned int *flags,
85		const struct ipt_entry *entry, unsigned int *nfcache,
86		struct ipt_entry_match **match)
87{
88	struct ipt_addrtype_info *info =
89		(struct ipt_addrtype_info *) (*match)->data;
90
91	switch (c) {
92	case '1':
93		if (*flags&IPT_ADDRTYPE_OPT_SRCTYPE)
94			exit_error(PARAMETER_PROBLEM,
95			           "addrtype: can't specify src-type twice");
96		check_inverse(optarg, &invert, &optind, 0);
97		parse_types(argv[optind-1], &info->source);
98		if (invert)
99			info->invert_source = 1;
100		*flags |= IPT_ADDRTYPE_OPT_SRCTYPE;
101		break;
102	case '2':
103		if (*flags&IPT_ADDRTYPE_OPT_DSTTYPE)
104			exit_error(PARAMETER_PROBLEM,
105			           "addrtype: can't specify dst-type twice");
106		check_inverse(optarg, &invert, &optind, 0);
107		parse_types(argv[optind-1], &info->dest);
108		if (invert)
109			info->invert_dest = 1;
110		*flags |= IPT_ADDRTYPE_OPT_DSTTYPE;
111		break;
112	default:
113		return 0;
114	}
115
116	return 1;
117}
118
119static void final_check(unsigned int flags)
120{
121	if (!(flags & (IPT_ADDRTYPE_OPT_SRCTYPE|IPT_ADDRTYPE_OPT_DSTTYPE)))
122		exit_error(PARAMETER_PROBLEM,
123			   "addrtype: you must specify --src-type or --dst-type");
124}
125
126static void print_types(u_int16_t mask)
127{
128	const char *sep = "";
129	int i;
130
131	for (i = 0; rtn_names[i]; i++)
132		if (mask & (1 << i)) {
133			printf("%s%s", sep, rtn_names[i]);
134			sep = ",";
135		}
136
137	printf(" ");
138}
139
140static void print(const struct ipt_ip *ip,
141		const struct ipt_entry_match *match,
142		int numeric)
143{
144	const struct ipt_addrtype_info *info =
145		(struct ipt_addrtype_info *) match->data;
146
147	printf("ADDRTYPE match ");
148	if (info->source) {
149		printf("src-type ");
150		if (info->invert_source)
151			printf("!");
152		print_types(info->source);
153	}
154	if (info->dest) {
155		printf("dst-type ");
156		if (info->invert_dest)
157			printf("!");
158		print_types(info->dest);
159	}
160}
161
162static void save(const struct ipt_ip *ip,
163		const struct ipt_entry_match *match)
164{
165	const struct ipt_addrtype_info *info =
166		(struct ipt_addrtype_info *) match->data;
167
168	if (info->source) {
169		printf("--src-type ");
170		if (info->invert_source)
171			printf("! ");
172		print_types(info->source);
173	}
174	if (info->dest) {
175		printf("--dst-type ");
176		if (info->invert_dest)
177			printf("! ");
178		print_types(info->dest);
179	}
180}
181
182static struct option opts[] = {
183	{ "src-type", 1, 0, '1' },
184	{ "dst-type", 1, 0, '2' },
185	{ 0 }
186};
187
188static
189struct iptables_match addrtype = {
190	.next 		= NULL,
191	.name 		= "addrtype",
192	.version 	= IPTABLES_VERSION,
193	.size 		= IPT_ALIGN(sizeof(struct ipt_addrtype_info)),
194	.userspacesize 	= IPT_ALIGN(sizeof(struct ipt_addrtype_info)),
195	.help 		= &help,
196	.parse 		= &parse,
197	.final_check 	= &final_check,
198	.print 		= &print,
199	.save 		= &save,
200	.extra_opts 	= opts
201};
202
203
204void _init(void)
205{
206	register_match(&addrtype);
207}
208