xref: /freebsd/sbin/ipf/ipsend/ipsopt.c (revision 51e16cb8fc536913f490ac6bc9c17e92ebd0411b)
1 
2 /*
3  * Copyright (C) 2012 by Darren Reed.
4  *
5  * See the IPFILTER.LICENCE file for details on licencing.
6  *
7  */
8 #include <sys/param.h>
9 #include <sys/types.h>
10 #include <sys/time.h>
11 #include <sys/socket.h>
12 #include <netinet/in.h>
13 #include <netinet/in_systm.h>
14 #include <netinet/ip.h>
15 #include <stdio.h>
16 #include <string.h>
17 #include <stdlib.h>
18 #include <netinet/ip_var.h>
19 #include <netinet/tcp.h>
20 #include <arpa/inet.h>
21 #include "ipsend.h"
22 
23 
24 #ifndef	__P
25 #  define	__P(x)	x
26 #endif
27 
28 
29 struct ipopt_names ionames[] = {
30 	{ IPOPT_EOL,	0x01,	1, "eol" },
31 	{ IPOPT_NOP,	0x02,	1, "nop" },
32 	{ IPOPT_RR,	0x04,	3, "rr" },	/* 1 route */
33 	{ IPOPT_TS,	0x08,	8, "ts" },	/* 1 TS */
34 	{ IPOPT_SECURITY, 0x08,	11, "sec-level" },
35 	{ IPOPT_LSRR,	0x10,	7, "lsrr" },	/* 1 route */
36 	{ IPOPT_SATID,	0x20,	4, "satid" },
37 	{ IPOPT_SSRR,	0x40,	7, "ssrr" },	/* 1 route */
38 	{ 0, 0, 0, NULL }	/* must be last */
39 };
40 
41 struct	ipopt_names secnames[] = {
42 	{ IPOPT_SECUR_UNCLASS,	0x0100,	0, "unclass" },
43 	{ IPOPT_SECUR_CONFID,	0x0200,	0, "confid" },
44 	{ IPOPT_SECUR_EFTO,	0x0400,	0, "efto" },
45 	{ IPOPT_SECUR_MMMM,	0x0800,	0, "mmmm" },
46 	{ IPOPT_SECUR_RESTR,	0x1000,	0, "restr" },
47 	{ IPOPT_SECUR_SECRET,	0x2000,	0, "secret" },
48 	{ IPOPT_SECUR_TOPSECRET, 0x4000,0, "topsecret" },
49 	{ 0, 0, 0, NULL }	/* must be last */
50 };
51 
52 
ipseclevel(slevel)53 u_short ipseclevel(slevel)
54 	char *slevel;
55 {
56 	struct ipopt_names *so;
57 
58 	for (so = secnames; so->on_name; so++)
59 		if (!strcasecmp(slevel, so->on_name))
60 			break;
61 
62 	if (!so->on_name) {
63 		fprintf(stderr, "no such security level: %s\n", slevel);
64 		return (0);
65 	}
66 	return (so->on_value);
67 }
68 
69 
70 int
addipopt(char * op,struct ipopt_names * io,int len,char * class)71 addipopt(char *op, struct ipopt_names *io, int len, char *class)
72 {
73 	struct in_addr ipadr;
74 	int olen = len, srr = 0;
75 	u_short val;
76 	u_char lvl;
77 	char *s = op, *t;
78 
79 	if ((len + io->on_siz) > 48) {
80 		fprintf(stderr, "options too long\n");
81 		return (0);
82 	}
83 	len += io->on_siz;
84 	*op++ = io->on_value;
85 	if (io->on_siz > 1) {
86 		/*
87 		 * Allow option to specify RR buffer length in bytes.
88 		 */
89 		if (io->on_value == IPOPT_RR) {
90 			val = (class && *class) ? atoi(class) : 4;
91 			*op++ = val + io->on_siz;
92 			len += val;
93 		} else
94 			*op++ = io->on_siz;
95 		if (io->on_value == IPOPT_TS)
96 			*op++ = IPOPT_MINOFF + 1;
97 		else
98 			*op++ = IPOPT_MINOFF;
99 
100 		while (class && *class) {
101 			t = NULL;
102 			switch (io->on_value)
103 			{
104 			case IPOPT_SECURITY :
105 				lvl = ipseclevel(class);
106 				*(op - 1) = lvl;
107 				break;
108 			case IPOPT_LSRR :
109 			case IPOPT_SSRR :
110 				if ((t = strchr(class, ',')))
111 					*t = '\0';
112 				ipadr.s_addr = inet_addr(class);
113 				srr++;
114 				bcopy((char *)&ipadr, op, sizeof(ipadr));
115 				op += sizeof(ipadr);
116 				break;
117 			case IPOPT_SATID :
118 				val = atoi(class);
119 				bcopy((char *)&val, op, 2);
120 				break;
121 			}
122 
123 			if (t)
124 				*t++ = ',';
125 			class = t;
126 		}
127 		if (srr)
128 			s[IPOPT_OLEN] = IPOPT_MINOFF - 1 + 4 * srr;
129 		if (io->on_value == IPOPT_RR)
130 			op += val;
131 		else
132 			op += io->on_siz - 3;
133 	}
134 	return (len - olen);
135 }
136 
137 
138 u_32_t
139 buildopts(char *cp, char *op, int len)
140 	char *cp, *op;
141 	int len;
142 {
143 	struct ipopt_names *io;
144 	u_32_t msk = 0;
145 	char *s, *t;
146 	int inc, lastop = -1;
147 
148 	for (s = strtok(cp, ","); s; s = strtok(NULL, ",")) {
149 		if ((t = strchr(s, '=')))
150 			*t++ = '\0';
151 		for (io = ionames; io->on_name; io++) {
152 			if (strcasecmp(s, io->on_name) || (msk & io->on_bit))
153 				continue;
154 			lastop = io->on_value;
155 			if ((inc = addipopt(op, io, len, t))) {
156 				op += inc;
157 				len += inc;
158 			}
159 			msk |= io->on_bit;
160 			break;
161 		}
162 		if (!io->on_name) {
163 			fprintf(stderr, "unknown IP option name %s\n", s);
164 			return (0);
165 		}
166 	}
167 
168 	if (len & 3) {
169 		while (len & 3) {
170 			*op++ = ((len & 3) == 3) ? IPOPT_EOL : IPOPT_NOP;
171 			len++;
172 		}
173 	} else {
174 		if (lastop != IPOPT_EOL) {
175 			if (lastop == IPOPT_NOP)
176 				*(op - 1) = IPOPT_EOL;
177 			else {
178 				*op++ = IPOPT_NOP;
179 				*op++ = IPOPT_NOP;
180 				*op++ = IPOPT_NOP;
181 				*op = IPOPT_EOL;
182 				len += 4;
183 			}
184 		}
185 	}
186 	return (len);
187 }
188