1a9643ea8Slogwang /*-
2*22ce4affSfengbojiang * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3*22ce4affSfengbojiang *
4a9643ea8Slogwang * Copyright (c) 2001 Charles Mott <[email protected]>
5a9643ea8Slogwang * All rights reserved.
6a9643ea8Slogwang *
7a9643ea8Slogwang * Redistribution and use in source and binary forms, with or without
8a9643ea8Slogwang * modification, are permitted provided that the following conditions
9a9643ea8Slogwang * are met:
10a9643ea8Slogwang * 1. Redistributions of source code must retain the above copyright
11a9643ea8Slogwang * notice, this list of conditions and the following disclaimer.
12a9643ea8Slogwang * 2. Redistributions in binary form must reproduce the above copyright
13a9643ea8Slogwang * notice, this list of conditions and the following disclaimer in the
14a9643ea8Slogwang * documentation and/or other materials provided with the distribution.
15a9643ea8Slogwang *
16a9643ea8Slogwang * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17a9643ea8Slogwang * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18a9643ea8Slogwang * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19a9643ea8Slogwang * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20a9643ea8Slogwang * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21a9643ea8Slogwang * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22a9643ea8Slogwang * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23a9643ea8Slogwang * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24a9643ea8Slogwang * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25a9643ea8Slogwang * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26a9643ea8Slogwang * SUCH DAMAGE.
27a9643ea8Slogwang */
28a9643ea8Slogwang
29a9643ea8Slogwang #include <sys/cdefs.h>
30a9643ea8Slogwang __FBSDID("$FreeBSD$");
31a9643ea8Slogwang
32a9643ea8Slogwang /* file: alias_proxy.c
33a9643ea8Slogwang
34a9643ea8Slogwang This file encapsulates special operations related to transparent
35a9643ea8Slogwang proxy redirection. This is where packets with a particular destination,
36a9643ea8Slogwang usually tcp port 80, are redirected to a proxy server.
37a9643ea8Slogwang
38a9643ea8Slogwang When packets are proxied, the destination address and port are
39a9643ea8Slogwang modified. In certain cases, it is necessary to somehow encode
40a9643ea8Slogwang the original address/port info into the packet. Two methods are
41a9643ea8Slogwang presently supported: addition of a [DEST addr port] string at the
42a9643ea8Slogwang beginning of a tcp stream, or inclusion of an optional field
43a9643ea8Slogwang in the IP header.
44a9643ea8Slogwang
45a9643ea8Slogwang There is one public API function:
46a9643ea8Slogwang
47a9643ea8Slogwang PacketAliasProxyRule() -- Adds and deletes proxy
48a9643ea8Slogwang rules.
49a9643ea8Slogwang
50a9643ea8Slogwang Rules are stored in a linear linked list, so lookup efficiency
51a9643ea8Slogwang won't be too good for large lists.
52a9643ea8Slogwang
53a9643ea8Slogwang Initial development: April, 1998 (cjm)
54a9643ea8Slogwang */
55a9643ea8Slogwang
56a9643ea8Slogwang /* System includes */
57a9643ea8Slogwang #ifdef _KERNEL
58a9643ea8Slogwang #include <sys/param.h>
59a9643ea8Slogwang #include <sys/ctype.h>
60a9643ea8Slogwang #include <sys/libkern.h>
61a9643ea8Slogwang #include <sys/limits.h>
62a9643ea8Slogwang #else
63a9643ea8Slogwang #include <sys/types.h>
64a9643ea8Slogwang #include <ctype.h>
65a9643ea8Slogwang #include <stdio.h>
66a9643ea8Slogwang #include <stdlib.h>
67a9643ea8Slogwang #include <netdb.h>
68a9643ea8Slogwang #include <string.h>
69a9643ea8Slogwang #endif
70a9643ea8Slogwang
71a9643ea8Slogwang #include <netinet/tcp.h>
72a9643ea8Slogwang
73a9643ea8Slogwang #ifdef _KERNEL
74a9643ea8Slogwang #include <netinet/libalias/alias.h>
75a9643ea8Slogwang #include <netinet/libalias/alias_local.h>
76a9643ea8Slogwang #include <netinet/libalias/alias_mod.h>
77a9643ea8Slogwang #else
78a9643ea8Slogwang #include <arpa/inet.h>
79a9643ea8Slogwang #include "alias.h" /* Public API functions for libalias */
80a9643ea8Slogwang #include "alias_local.h" /* Functions used by alias*.c */
81a9643ea8Slogwang #endif
82a9643ea8Slogwang
83a9643ea8Slogwang /*
84a9643ea8Slogwang Data structures
85a9643ea8Slogwang */
86a9643ea8Slogwang
87a9643ea8Slogwang /*
88a9643ea8Slogwang * A linked list of arbitrary length, based on struct proxy_entry is
89a9643ea8Slogwang * used to store proxy rules.
90a9643ea8Slogwang */
91a9643ea8Slogwang struct proxy_entry {
92a9643ea8Slogwang struct libalias *la;
93a9643ea8Slogwang #define PROXY_TYPE_ENCODE_NONE 1
94a9643ea8Slogwang #define PROXY_TYPE_ENCODE_TCPSTREAM 2
95a9643ea8Slogwang #define PROXY_TYPE_ENCODE_IPHDR 3
96a9643ea8Slogwang int rule_index;
97a9643ea8Slogwang int proxy_type;
98a9643ea8Slogwang u_char proto;
99a9643ea8Slogwang u_short proxy_port;
100a9643ea8Slogwang u_short server_port;
101a9643ea8Slogwang
102a9643ea8Slogwang struct in_addr server_addr;
103a9643ea8Slogwang
104a9643ea8Slogwang struct in_addr src_addr;
105a9643ea8Slogwang struct in_addr src_mask;
106a9643ea8Slogwang
107a9643ea8Slogwang struct in_addr dst_addr;
108a9643ea8Slogwang struct in_addr dst_mask;
109a9643ea8Slogwang
110a9643ea8Slogwang struct proxy_entry *next;
111a9643ea8Slogwang struct proxy_entry *last;
112a9643ea8Slogwang };
113a9643ea8Slogwang
114a9643ea8Slogwang /*
115a9643ea8Slogwang File scope variables
116a9643ea8Slogwang */
117a9643ea8Slogwang
118a9643ea8Slogwang /* Local (static) functions:
119a9643ea8Slogwang
120a9643ea8Slogwang IpMask() -- Utility function for creating IP
121a9643ea8Slogwang masks from integer (1-32) specification.
122a9643ea8Slogwang IpAddr() -- Utility function for converting string
123a9643ea8Slogwang to IP address
124a9643ea8Slogwang IpPort() -- Utility function for converting string
125a9643ea8Slogwang to port number
126a9643ea8Slogwang RuleAdd() -- Adds an element to the rule list.
127a9643ea8Slogwang RuleDelete() -- Removes an element from the rule list.
128a9643ea8Slogwang RuleNumberDelete() -- Removes all elements from the rule list
129a9643ea8Slogwang having a certain rule number.
130a9643ea8Slogwang ProxyEncodeTcpStream() -- Adds [DEST x.x.x.x xxxx] to the beginning
131a9643ea8Slogwang of a TCP stream.
132a9643ea8Slogwang ProxyEncodeIpHeader() -- Adds an IP option indicating the true
133a9643ea8Slogwang destination of a proxied IP packet
134a9643ea8Slogwang */
135a9643ea8Slogwang
136a9643ea8Slogwang static int IpMask(int, struct in_addr *);
137a9643ea8Slogwang static int IpAddr(char *, struct in_addr *);
138a9643ea8Slogwang static int IpPort(char *, int, int *);
139a9643ea8Slogwang static void RuleAdd(struct libalias *la, struct proxy_entry *);
140a9643ea8Slogwang static void RuleDelete(struct proxy_entry *);
141a9643ea8Slogwang static int RuleNumberDelete(struct libalias *la, int);
142a9643ea8Slogwang static void ProxyEncodeTcpStream(struct alias_link *, struct ip *, int);
143a9643ea8Slogwang static void ProxyEncodeIpHeader(struct ip *, int);
144a9643ea8Slogwang
145a9643ea8Slogwang static int
IpMask(int nbits,struct in_addr * mask)146a9643ea8Slogwang IpMask(int nbits, struct in_addr *mask)
147a9643ea8Slogwang {
148a9643ea8Slogwang int i;
149a9643ea8Slogwang u_int imask;
150a9643ea8Slogwang
151a9643ea8Slogwang if (nbits < 0 || nbits > 32)
152a9643ea8Slogwang return (-1);
153a9643ea8Slogwang
154a9643ea8Slogwang imask = 0;
155a9643ea8Slogwang for (i = 0; i < nbits; i++)
156a9643ea8Slogwang imask = (imask >> 1) + 0x80000000;
157a9643ea8Slogwang mask->s_addr = htonl(imask);
158a9643ea8Slogwang
159a9643ea8Slogwang return (0);
160a9643ea8Slogwang }
161a9643ea8Slogwang
162a9643ea8Slogwang static int
IpAddr(char * s,struct in_addr * addr)163a9643ea8Slogwang IpAddr(char *s, struct in_addr *addr)
164a9643ea8Slogwang {
165a9643ea8Slogwang if (inet_aton(s, addr) == 0)
166a9643ea8Slogwang return (-1);
167a9643ea8Slogwang else
168a9643ea8Slogwang return (0);
169a9643ea8Slogwang }
170a9643ea8Slogwang
171a9643ea8Slogwang static int
IpPort(char * s,int proto,int * port)172a9643ea8Slogwang IpPort(char *s, int proto, int *port)
173a9643ea8Slogwang {
174a9643ea8Slogwang int n;
175a9643ea8Slogwang
176a9643ea8Slogwang n = sscanf(s, "%d", port);
177a9643ea8Slogwang if (n != 1)
178a9643ea8Slogwang #ifndef _KERNEL /* XXX: we accept only numeric ports in kernel */
179a9643ea8Slogwang {
180a9643ea8Slogwang struct servent *se;
181a9643ea8Slogwang
182a9643ea8Slogwang if (proto == IPPROTO_TCP)
183a9643ea8Slogwang se = getservbyname(s, "tcp");
184a9643ea8Slogwang else if (proto == IPPROTO_UDP)
185a9643ea8Slogwang se = getservbyname(s, "udp");
186a9643ea8Slogwang else
187a9643ea8Slogwang return (-1);
188a9643ea8Slogwang
189a9643ea8Slogwang if (se == NULL)
190a9643ea8Slogwang return (-1);
191a9643ea8Slogwang
192a9643ea8Slogwang *port = (u_int) ntohs(se->s_port);
193a9643ea8Slogwang }
194a9643ea8Slogwang #else
195a9643ea8Slogwang return (-1);
196a9643ea8Slogwang #endif
197a9643ea8Slogwang return (0);
198a9643ea8Slogwang }
199a9643ea8Slogwang
200a9643ea8Slogwang void
RuleAdd(struct libalias * la,struct proxy_entry * entry)201a9643ea8Slogwang RuleAdd(struct libalias *la, struct proxy_entry *entry)
202a9643ea8Slogwang {
203a9643ea8Slogwang int rule_index;
204a9643ea8Slogwang struct proxy_entry *ptr;
205a9643ea8Slogwang struct proxy_entry *ptr_last;
206a9643ea8Slogwang
207a9643ea8Slogwang LIBALIAS_LOCK_ASSERT(la);
208a9643ea8Slogwang
209a9643ea8Slogwang entry->la = la;
210a9643ea8Slogwang if (la->proxyList == NULL) {
211a9643ea8Slogwang la->proxyList = entry;
212a9643ea8Slogwang entry->last = NULL;
213a9643ea8Slogwang entry->next = NULL;
214a9643ea8Slogwang return;
215a9643ea8Slogwang }
216a9643ea8Slogwang
217a9643ea8Slogwang rule_index = entry->rule_index;
218a9643ea8Slogwang ptr = la->proxyList;
219a9643ea8Slogwang ptr_last = NULL;
220a9643ea8Slogwang while (ptr != NULL) {
221a9643ea8Slogwang if (ptr->rule_index >= rule_index) {
222a9643ea8Slogwang if (ptr_last == NULL) {
223a9643ea8Slogwang entry->next = la->proxyList;
224a9643ea8Slogwang entry->last = NULL;
225a9643ea8Slogwang la->proxyList->last = entry;
226a9643ea8Slogwang la->proxyList = entry;
227a9643ea8Slogwang return;
228a9643ea8Slogwang }
229a9643ea8Slogwang ptr_last->next = entry;
230a9643ea8Slogwang ptr->last = entry;
231a9643ea8Slogwang entry->last = ptr->last;
232a9643ea8Slogwang entry->next = ptr;
233a9643ea8Slogwang return;
234a9643ea8Slogwang }
235a9643ea8Slogwang ptr_last = ptr;
236a9643ea8Slogwang ptr = ptr->next;
237a9643ea8Slogwang }
238a9643ea8Slogwang
239a9643ea8Slogwang ptr_last->next = entry;
240a9643ea8Slogwang entry->last = ptr_last;
241a9643ea8Slogwang entry->next = NULL;
242a9643ea8Slogwang }
243a9643ea8Slogwang
244a9643ea8Slogwang static void
RuleDelete(struct proxy_entry * entry)245a9643ea8Slogwang RuleDelete(struct proxy_entry *entry)
246a9643ea8Slogwang {
247a9643ea8Slogwang struct libalias *la;
248a9643ea8Slogwang
249a9643ea8Slogwang la = entry->la;
250a9643ea8Slogwang LIBALIAS_LOCK_ASSERT(la);
251a9643ea8Slogwang if (entry->last != NULL)
252a9643ea8Slogwang entry->last->next = entry->next;
253a9643ea8Slogwang else
254a9643ea8Slogwang la->proxyList = entry->next;
255a9643ea8Slogwang
256a9643ea8Slogwang if (entry->next != NULL)
257a9643ea8Slogwang entry->next->last = entry->last;
258a9643ea8Slogwang
259a9643ea8Slogwang free(entry);
260a9643ea8Slogwang }
261a9643ea8Slogwang
262a9643ea8Slogwang static int
RuleNumberDelete(struct libalias * la,int rule_index)263a9643ea8Slogwang RuleNumberDelete(struct libalias *la, int rule_index)
264a9643ea8Slogwang {
265a9643ea8Slogwang int err;
266a9643ea8Slogwang struct proxy_entry *ptr;
267a9643ea8Slogwang
268a9643ea8Slogwang LIBALIAS_LOCK_ASSERT(la);
269a9643ea8Slogwang err = -1;
270a9643ea8Slogwang ptr = la->proxyList;
271a9643ea8Slogwang while (ptr != NULL) {
272a9643ea8Slogwang struct proxy_entry *ptr_next;
273a9643ea8Slogwang
274a9643ea8Slogwang ptr_next = ptr->next;
275a9643ea8Slogwang if (ptr->rule_index == rule_index) {
276a9643ea8Slogwang err = 0;
277a9643ea8Slogwang RuleDelete(ptr);
278a9643ea8Slogwang }
279a9643ea8Slogwang ptr = ptr_next;
280a9643ea8Slogwang }
281a9643ea8Slogwang
282a9643ea8Slogwang return (err);
283a9643ea8Slogwang }
284a9643ea8Slogwang
285a9643ea8Slogwang static void
ProxyEncodeTcpStream(struct alias_link * lnk,struct ip * pip,int maxpacketsize)286a9643ea8Slogwang ProxyEncodeTcpStream(struct alias_link *lnk,
287a9643ea8Slogwang struct ip *pip,
288a9643ea8Slogwang int maxpacketsize)
289a9643ea8Slogwang {
290a9643ea8Slogwang int slen;
291a9643ea8Slogwang char buffer[40];
292a9643ea8Slogwang struct tcphdr *tc;
293*22ce4affSfengbojiang char addrbuf[INET_ADDRSTRLEN];
294a9643ea8Slogwang
295a9643ea8Slogwang /* Compute pointer to tcp header */
296a9643ea8Slogwang tc = (struct tcphdr *)ip_next(pip);
297a9643ea8Slogwang
298a9643ea8Slogwang /* Don't modify if once already modified */
299a9643ea8Slogwang
300a9643ea8Slogwang if (GetAckModified(lnk))
301a9643ea8Slogwang return;
302a9643ea8Slogwang
303a9643ea8Slogwang /* Translate destination address and port to string form */
304a9643ea8Slogwang snprintf(buffer, sizeof(buffer) - 2, "[DEST %s %d]",
305*22ce4affSfengbojiang inet_ntoa_r(GetProxyAddress(lnk), INET_NTOA_BUF(addrbuf)),
306*22ce4affSfengbojiang (u_int) ntohs(GetProxyPort(lnk)));
307a9643ea8Slogwang
308a9643ea8Slogwang /* Pad string out to a multiple of two in length */
309a9643ea8Slogwang slen = strlen(buffer);
310a9643ea8Slogwang switch (slen % 2) {
311a9643ea8Slogwang case 0:
312a9643ea8Slogwang strcat(buffer, " \n");
313a9643ea8Slogwang slen += 2;
314a9643ea8Slogwang break;
315a9643ea8Slogwang case 1:
316a9643ea8Slogwang strcat(buffer, "\n");
317a9643ea8Slogwang slen += 1;
318a9643ea8Slogwang }
319a9643ea8Slogwang
320a9643ea8Slogwang /* Check for packet overflow */
321a9643ea8Slogwang if ((int)(ntohs(pip->ip_len) + strlen(buffer)) > maxpacketsize)
322a9643ea8Slogwang return;
323a9643ea8Slogwang
324a9643ea8Slogwang /* Shift existing TCP data and insert destination string */
325a9643ea8Slogwang {
326a9643ea8Slogwang int dlen;
327a9643ea8Slogwang int hlen;
328a9643ea8Slogwang char *p;
329a9643ea8Slogwang
330a9643ea8Slogwang hlen = (pip->ip_hl + tc->th_off) << 2;
331a9643ea8Slogwang dlen = ntohs(pip->ip_len) - hlen;
332a9643ea8Slogwang
333a9643ea8Slogwang /* Modify first packet that has data in it */
334a9643ea8Slogwang
335a9643ea8Slogwang if (dlen == 0)
336a9643ea8Slogwang return;
337a9643ea8Slogwang
338a9643ea8Slogwang p = (char *)pip;
339a9643ea8Slogwang p += hlen;
340a9643ea8Slogwang
341a9643ea8Slogwang bcopy(p, p + slen, dlen);
342a9643ea8Slogwang memcpy(p, buffer, slen);
343a9643ea8Slogwang }
344a9643ea8Slogwang
345a9643ea8Slogwang /* Save information about modfied sequence number */
346a9643ea8Slogwang {
347a9643ea8Slogwang int delta;
348a9643ea8Slogwang
349a9643ea8Slogwang SetAckModified(lnk);
350a9643ea8Slogwang tc = (struct tcphdr *)ip_next(pip);
351a9643ea8Slogwang delta = GetDeltaSeqOut(tc->th_seq, lnk);
352a9643ea8Slogwang AddSeq(lnk, delta + slen, pip->ip_hl, pip->ip_len, tc->th_seq,
353a9643ea8Slogwang tc->th_off);
354a9643ea8Slogwang }
355a9643ea8Slogwang
356a9643ea8Slogwang /* Update IP header packet length and checksum */
357a9643ea8Slogwang {
358a9643ea8Slogwang int accumulate;
359a9643ea8Slogwang
360a9643ea8Slogwang accumulate = pip->ip_len;
361a9643ea8Slogwang pip->ip_len = htons(ntohs(pip->ip_len) + slen);
362a9643ea8Slogwang accumulate -= pip->ip_len;
363a9643ea8Slogwang
364a9643ea8Slogwang ADJUST_CHECKSUM(accumulate, pip->ip_sum);
365a9643ea8Slogwang }
366a9643ea8Slogwang
367a9643ea8Slogwang /* Update TCP checksum, Use TcpChecksum since so many things have
368a9643ea8Slogwang already changed. */
369a9643ea8Slogwang
370a9643ea8Slogwang tc->th_sum = 0;
371a9643ea8Slogwang #ifdef _KERNEL
372a9643ea8Slogwang tc->th_x2 = 1;
373a9643ea8Slogwang #else
374a9643ea8Slogwang tc->th_sum = TcpChecksum(pip);
375a9643ea8Slogwang #endif
376a9643ea8Slogwang }
377a9643ea8Slogwang
378a9643ea8Slogwang static void
ProxyEncodeIpHeader(struct ip * pip,int maxpacketsize)379a9643ea8Slogwang ProxyEncodeIpHeader(struct ip *pip,
380a9643ea8Slogwang int maxpacketsize)
381a9643ea8Slogwang {
382a9643ea8Slogwang #define OPTION_LEN_BYTES 8
383a9643ea8Slogwang #define OPTION_LEN_INT16 4
384a9643ea8Slogwang #define OPTION_LEN_INT32 2
385*22ce4affSfengbojiang _Alignas(_Alignof(u_short)) u_char option[OPTION_LEN_BYTES];
386a9643ea8Slogwang
387a9643ea8Slogwang #ifdef LIBALIAS_DEBUG
388a9643ea8Slogwang fprintf(stdout, " ip cksum 1 = %x\n", (u_int) IpChecksum(pip));
389a9643ea8Slogwang fprintf(stdout, "tcp cksum 1 = %x\n", (u_int) TcpChecksum(pip));
390a9643ea8Slogwang #endif
391a9643ea8Slogwang
392a9643ea8Slogwang (void)maxpacketsize;
393a9643ea8Slogwang
394a9643ea8Slogwang /* Check to see that there is room to add an IP option */
395a9643ea8Slogwang if (pip->ip_hl > (0x0f - OPTION_LEN_INT32))
396a9643ea8Slogwang return;
397a9643ea8Slogwang
398a9643ea8Slogwang /* Build option and copy into packet */
399a9643ea8Slogwang {
400a9643ea8Slogwang u_char *ptr;
401a9643ea8Slogwang struct tcphdr *tc;
402a9643ea8Slogwang
403a9643ea8Slogwang ptr = (u_char *) pip;
404a9643ea8Slogwang ptr += 20;
405a9643ea8Slogwang memcpy(ptr + OPTION_LEN_BYTES, ptr, ntohs(pip->ip_len) - 20);
406a9643ea8Slogwang
407a9643ea8Slogwang option[0] = 0x64; /* class: 3 (reserved), option 4 */
408a9643ea8Slogwang option[1] = OPTION_LEN_BYTES;
409a9643ea8Slogwang
410a9643ea8Slogwang memcpy(&option[2], (u_char *) & pip->ip_dst, 4);
411a9643ea8Slogwang
412a9643ea8Slogwang tc = (struct tcphdr *)ip_next(pip);
413a9643ea8Slogwang memcpy(&option[6], (u_char *) & tc->th_sport, 2);
414a9643ea8Slogwang
415a9643ea8Slogwang memcpy(ptr, option, 8);
416a9643ea8Slogwang }
417a9643ea8Slogwang
418a9643ea8Slogwang /* Update checksum, header length and packet length */
419a9643ea8Slogwang {
420a9643ea8Slogwang int i;
421a9643ea8Slogwang int accumulate;
422a9643ea8Slogwang u_short *sptr;
423a9643ea8Slogwang
424a9643ea8Slogwang sptr = (u_short *) option;
425a9643ea8Slogwang accumulate = 0;
426a9643ea8Slogwang for (i = 0; i < OPTION_LEN_INT16; i++)
427a9643ea8Slogwang accumulate -= *(sptr++);
428a9643ea8Slogwang
429a9643ea8Slogwang sptr = (u_short *) pip;
430a9643ea8Slogwang accumulate += *sptr;
431a9643ea8Slogwang pip->ip_hl += OPTION_LEN_INT32;
432a9643ea8Slogwang accumulate -= *sptr;
433a9643ea8Slogwang
434a9643ea8Slogwang accumulate += pip->ip_len;
435a9643ea8Slogwang pip->ip_len = htons(ntohs(pip->ip_len) + OPTION_LEN_BYTES);
436a9643ea8Slogwang accumulate -= pip->ip_len;
437a9643ea8Slogwang
438a9643ea8Slogwang ADJUST_CHECKSUM(accumulate, pip->ip_sum);
439a9643ea8Slogwang }
440a9643ea8Slogwang #undef OPTION_LEN_BYTES
441a9643ea8Slogwang #undef OPTION_LEN_INT16
442a9643ea8Slogwang #undef OPTION_LEN_INT32
443a9643ea8Slogwang #ifdef LIBALIAS_DEBUG
444a9643ea8Slogwang fprintf(stdout, " ip cksum 2 = %x\n", (u_int) IpChecksum(pip));
445a9643ea8Slogwang fprintf(stdout, "tcp cksum 2 = %x\n", (u_int) TcpChecksum(pip));
446a9643ea8Slogwang #endif
447a9643ea8Slogwang }
448a9643ea8Slogwang
449a9643ea8Slogwang /* Functions by other packet alias source files
450a9643ea8Slogwang
451a9643ea8Slogwang ProxyCheck() -- Checks whether an outgoing packet should
452a9643ea8Slogwang be proxied.
453a9643ea8Slogwang ProxyModify() -- Encodes the original destination address/port
454a9643ea8Slogwang for a packet which is to be redirected to
455a9643ea8Slogwang a proxy server.
456a9643ea8Slogwang */
457a9643ea8Slogwang
458a9643ea8Slogwang int
ProxyCheck(struct libalias * la,struct in_addr * proxy_server_addr,u_short * proxy_server_port,struct in_addr src_addr,struct in_addr dst_addr,u_short dst_port,u_char ip_p)459a9643ea8Slogwang ProxyCheck(struct libalias *la, struct in_addr *proxy_server_addr,
460a9643ea8Slogwang u_short * proxy_server_port, struct in_addr src_addr,
461a9643ea8Slogwang struct in_addr dst_addr, u_short dst_port, u_char ip_p)
462a9643ea8Slogwang {
463a9643ea8Slogwang struct proxy_entry *ptr;
464a9643ea8Slogwang
465a9643ea8Slogwang LIBALIAS_LOCK_ASSERT(la);
466a9643ea8Slogwang
467a9643ea8Slogwang ptr = la->proxyList;
468a9643ea8Slogwang while (ptr != NULL) {
469a9643ea8Slogwang u_short proxy_port;
470a9643ea8Slogwang
471a9643ea8Slogwang proxy_port = ptr->proxy_port;
472a9643ea8Slogwang if ((dst_port == proxy_port || proxy_port == 0)
473a9643ea8Slogwang && ip_p == ptr->proto
474a9643ea8Slogwang && src_addr.s_addr != ptr->server_addr.s_addr) {
475a9643ea8Slogwang struct in_addr src_addr_masked;
476a9643ea8Slogwang struct in_addr dst_addr_masked;
477a9643ea8Slogwang
478a9643ea8Slogwang src_addr_masked.s_addr = src_addr.s_addr & ptr->src_mask.s_addr;
479a9643ea8Slogwang dst_addr_masked.s_addr = dst_addr.s_addr & ptr->dst_mask.s_addr;
480a9643ea8Slogwang
481a9643ea8Slogwang if ((src_addr_masked.s_addr == ptr->src_addr.s_addr)
482a9643ea8Slogwang && (dst_addr_masked.s_addr == ptr->dst_addr.s_addr)) {
483a9643ea8Slogwang if ((*proxy_server_port = ptr->server_port) == 0)
484a9643ea8Slogwang *proxy_server_port = dst_port;
485a9643ea8Slogwang *proxy_server_addr = ptr->server_addr;
486a9643ea8Slogwang return (ptr->proxy_type);
487a9643ea8Slogwang }
488a9643ea8Slogwang }
489a9643ea8Slogwang ptr = ptr->next;
490a9643ea8Slogwang }
491a9643ea8Slogwang
492a9643ea8Slogwang return (0);
493a9643ea8Slogwang }
494a9643ea8Slogwang
495a9643ea8Slogwang void
ProxyModify(struct libalias * la,struct alias_link * lnk,struct ip * pip,int maxpacketsize,int proxy_type)496a9643ea8Slogwang ProxyModify(struct libalias *la, struct alias_link *lnk,
497a9643ea8Slogwang struct ip *pip,
498a9643ea8Slogwang int maxpacketsize,
499a9643ea8Slogwang int proxy_type)
500a9643ea8Slogwang {
501a9643ea8Slogwang
502a9643ea8Slogwang LIBALIAS_LOCK_ASSERT(la);
503a9643ea8Slogwang (void)la;
504a9643ea8Slogwang
505a9643ea8Slogwang switch (proxy_type) {
506a9643ea8Slogwang case PROXY_TYPE_ENCODE_IPHDR:
507a9643ea8Slogwang ProxyEncodeIpHeader(pip, maxpacketsize);
508a9643ea8Slogwang break;
509a9643ea8Slogwang
510a9643ea8Slogwang case PROXY_TYPE_ENCODE_TCPSTREAM:
511a9643ea8Slogwang ProxyEncodeTcpStream(lnk, pip, maxpacketsize);
512a9643ea8Slogwang break;
513a9643ea8Slogwang }
514a9643ea8Slogwang }
515a9643ea8Slogwang
516a9643ea8Slogwang /*
517a9643ea8Slogwang Public API functions
518a9643ea8Slogwang */
519a9643ea8Slogwang
520a9643ea8Slogwang int
LibAliasProxyRule(struct libalias * la,const char * cmd)521a9643ea8Slogwang LibAliasProxyRule(struct libalias *la, const char *cmd)
522a9643ea8Slogwang {
523a9643ea8Slogwang /*
524a9643ea8Slogwang * This function takes command strings of the form:
525a9643ea8Slogwang *
526a9643ea8Slogwang * server <addr>[:<port>]
527a9643ea8Slogwang * [port <port>]
528a9643ea8Slogwang * [rule n]
529a9643ea8Slogwang * [proto tcp|udp]
530a9643ea8Slogwang * [src <addr>[/n]]
531a9643ea8Slogwang * [dst <addr>[/n]]
532a9643ea8Slogwang * [type encode_tcp_stream|encode_ip_hdr|no_encode]
533a9643ea8Slogwang *
534a9643ea8Slogwang * delete <rule number>
535a9643ea8Slogwang *
536a9643ea8Slogwang * Subfields can be in arbitrary order. Port numbers and addresses
537a9643ea8Slogwang * must be in either numeric or symbolic form. An optional rule number
538a9643ea8Slogwang * is used to control the order in which rules are searched. If two
539a9643ea8Slogwang * rules have the same number, then search order cannot be guaranteed,
540a9643ea8Slogwang * and the rules should be disjoint. If no rule number is specified,
541a9643ea8Slogwang * then 0 is used, and group 0 rules are always checked before any
542a9643ea8Slogwang * others.
543a9643ea8Slogwang */
544a9643ea8Slogwang int i, n, len, ret;
545a9643ea8Slogwang int cmd_len;
546a9643ea8Slogwang int token_count;
547a9643ea8Slogwang int state;
548a9643ea8Slogwang char *token;
549a9643ea8Slogwang char buffer[256];
550a9643ea8Slogwang char str_port[sizeof(buffer)];
551a9643ea8Slogwang char str_server_port[sizeof(buffer)];
552a9643ea8Slogwang char *res = buffer;
553a9643ea8Slogwang
554a9643ea8Slogwang int rule_index;
555a9643ea8Slogwang int proto;
556a9643ea8Slogwang int proxy_type;
557a9643ea8Slogwang int proxy_port;
558a9643ea8Slogwang int server_port;
559a9643ea8Slogwang struct in_addr server_addr;
560a9643ea8Slogwang struct in_addr src_addr, src_mask;
561a9643ea8Slogwang struct in_addr dst_addr, dst_mask;
562a9643ea8Slogwang struct proxy_entry *proxy_entry;
563a9643ea8Slogwang
564a9643ea8Slogwang LIBALIAS_LOCK(la);
565a9643ea8Slogwang ret = 0;
566a9643ea8Slogwang /* Copy command line into a buffer */
567a9643ea8Slogwang cmd += strspn(cmd, " \t");
568a9643ea8Slogwang cmd_len = strlen(cmd);
569a9643ea8Slogwang if (cmd_len > (int)(sizeof(buffer) - 1)) {
570a9643ea8Slogwang ret = -1;
571a9643ea8Slogwang goto getout;
572a9643ea8Slogwang }
573a9643ea8Slogwang strcpy(buffer, cmd);
574a9643ea8Slogwang
575a9643ea8Slogwang /* Convert to lower case */
576a9643ea8Slogwang len = strlen(buffer);
577a9643ea8Slogwang for (i = 0; i < len; i++)
578a9643ea8Slogwang buffer[i] = tolower((unsigned char)buffer[i]);
579a9643ea8Slogwang
580a9643ea8Slogwang /* Set default proxy type */
581a9643ea8Slogwang
582a9643ea8Slogwang /* Set up default values */
583a9643ea8Slogwang rule_index = 0;
584a9643ea8Slogwang proxy_type = PROXY_TYPE_ENCODE_NONE;
585a9643ea8Slogwang proto = IPPROTO_TCP;
586a9643ea8Slogwang proxy_port = 0;
587a9643ea8Slogwang server_addr.s_addr = 0;
588a9643ea8Slogwang server_port = 0;
589a9643ea8Slogwang src_addr.s_addr = 0;
590a9643ea8Slogwang IpMask(0, &src_mask);
591a9643ea8Slogwang dst_addr.s_addr = 0;
592a9643ea8Slogwang IpMask(0, &dst_mask);
593a9643ea8Slogwang
594a9643ea8Slogwang str_port[0] = 0;
595a9643ea8Slogwang str_server_port[0] = 0;
596a9643ea8Slogwang
597a9643ea8Slogwang /* Parse command string with state machine */
598a9643ea8Slogwang #define STATE_READ_KEYWORD 0
599a9643ea8Slogwang #define STATE_READ_TYPE 1
600a9643ea8Slogwang #define STATE_READ_PORT 2
601a9643ea8Slogwang #define STATE_READ_SERVER 3
602a9643ea8Slogwang #define STATE_READ_RULE 4
603a9643ea8Slogwang #define STATE_READ_DELETE 5
604a9643ea8Slogwang #define STATE_READ_PROTO 6
605a9643ea8Slogwang #define STATE_READ_SRC 7
606a9643ea8Slogwang #define STATE_READ_DST 8
607a9643ea8Slogwang state = STATE_READ_KEYWORD;
608a9643ea8Slogwang token = strsep(&res, " \t");
609a9643ea8Slogwang token_count = 0;
610a9643ea8Slogwang while (token != NULL) {
611a9643ea8Slogwang token_count++;
612a9643ea8Slogwang switch (state) {
613a9643ea8Slogwang case STATE_READ_KEYWORD:
614a9643ea8Slogwang if (strcmp(token, "type") == 0)
615a9643ea8Slogwang state = STATE_READ_TYPE;
616a9643ea8Slogwang else if (strcmp(token, "port") == 0)
617a9643ea8Slogwang state = STATE_READ_PORT;
618a9643ea8Slogwang else if (strcmp(token, "server") == 0)
619a9643ea8Slogwang state = STATE_READ_SERVER;
620a9643ea8Slogwang else if (strcmp(token, "rule") == 0)
621a9643ea8Slogwang state = STATE_READ_RULE;
622a9643ea8Slogwang else if (strcmp(token, "delete") == 0)
623a9643ea8Slogwang state = STATE_READ_DELETE;
624a9643ea8Slogwang else if (strcmp(token, "proto") == 0)
625a9643ea8Slogwang state = STATE_READ_PROTO;
626a9643ea8Slogwang else if (strcmp(token, "src") == 0)
627a9643ea8Slogwang state = STATE_READ_SRC;
628a9643ea8Slogwang else if (strcmp(token, "dst") == 0)
629a9643ea8Slogwang state = STATE_READ_DST;
630a9643ea8Slogwang else {
631a9643ea8Slogwang ret = -1;
632a9643ea8Slogwang goto getout;
633a9643ea8Slogwang }
634a9643ea8Slogwang break;
635a9643ea8Slogwang
636a9643ea8Slogwang case STATE_READ_TYPE:
637a9643ea8Slogwang if (strcmp(token, "encode_ip_hdr") == 0)
638a9643ea8Slogwang proxy_type = PROXY_TYPE_ENCODE_IPHDR;
639a9643ea8Slogwang else if (strcmp(token, "encode_tcp_stream") == 0)
640a9643ea8Slogwang proxy_type = PROXY_TYPE_ENCODE_TCPSTREAM;
641a9643ea8Slogwang else if (strcmp(token, "no_encode") == 0)
642a9643ea8Slogwang proxy_type = PROXY_TYPE_ENCODE_NONE;
643a9643ea8Slogwang else {
644a9643ea8Slogwang ret = -1;
645a9643ea8Slogwang goto getout;
646a9643ea8Slogwang }
647a9643ea8Slogwang state = STATE_READ_KEYWORD;
648a9643ea8Slogwang break;
649a9643ea8Slogwang
650a9643ea8Slogwang case STATE_READ_PORT:
651a9643ea8Slogwang strcpy(str_port, token);
652a9643ea8Slogwang state = STATE_READ_KEYWORD;
653a9643ea8Slogwang break;
654a9643ea8Slogwang
655a9643ea8Slogwang case STATE_READ_SERVER:
656a9643ea8Slogwang {
657a9643ea8Slogwang int err;
658a9643ea8Slogwang char *p;
659a9643ea8Slogwang char s[sizeof(buffer)];
660a9643ea8Slogwang
661a9643ea8Slogwang p = token;
662a9643ea8Slogwang while (*p != ':' && *p != 0)
663a9643ea8Slogwang p++;
664a9643ea8Slogwang
665a9643ea8Slogwang if (*p != ':') {
666a9643ea8Slogwang err = IpAddr(token, &server_addr);
667a9643ea8Slogwang if (err) {
668a9643ea8Slogwang ret = -1;
669a9643ea8Slogwang goto getout;
670a9643ea8Slogwang }
671a9643ea8Slogwang } else {
672a9643ea8Slogwang *p = ' ';
673a9643ea8Slogwang
674a9643ea8Slogwang n = sscanf(token, "%s %s", s, str_server_port);
675a9643ea8Slogwang if (n != 2) {
676a9643ea8Slogwang ret = -1;
677a9643ea8Slogwang goto getout;
678a9643ea8Slogwang }
679a9643ea8Slogwang
680a9643ea8Slogwang err = IpAddr(s, &server_addr);
681a9643ea8Slogwang if (err) {
682a9643ea8Slogwang ret = -1;
683a9643ea8Slogwang goto getout;
684a9643ea8Slogwang }
685a9643ea8Slogwang }
686a9643ea8Slogwang }
687a9643ea8Slogwang state = STATE_READ_KEYWORD;
688a9643ea8Slogwang break;
689a9643ea8Slogwang
690a9643ea8Slogwang case STATE_READ_RULE:
691a9643ea8Slogwang n = sscanf(token, "%d", &rule_index);
692a9643ea8Slogwang if (n != 1 || rule_index < 0) {
693a9643ea8Slogwang ret = -1;
694a9643ea8Slogwang goto getout;
695a9643ea8Slogwang }
696a9643ea8Slogwang state = STATE_READ_KEYWORD;
697a9643ea8Slogwang break;
698a9643ea8Slogwang
699a9643ea8Slogwang case STATE_READ_DELETE:
700a9643ea8Slogwang {
701a9643ea8Slogwang int err;
702a9643ea8Slogwang int rule_to_delete;
703a9643ea8Slogwang
704a9643ea8Slogwang if (token_count != 2) {
705a9643ea8Slogwang ret = -1;
706a9643ea8Slogwang goto getout;
707a9643ea8Slogwang }
708a9643ea8Slogwang
709a9643ea8Slogwang n = sscanf(token, "%d", &rule_to_delete);
710a9643ea8Slogwang if (n != 1) {
711a9643ea8Slogwang ret = -1;
712a9643ea8Slogwang goto getout;
713a9643ea8Slogwang }
714a9643ea8Slogwang err = RuleNumberDelete(la, rule_to_delete);
715a9643ea8Slogwang if (err)
716a9643ea8Slogwang ret = -1;
717*22ce4affSfengbojiang else
718a9643ea8Slogwang ret = 0;
719a9643ea8Slogwang goto getout;
720a9643ea8Slogwang }
721a9643ea8Slogwang
722a9643ea8Slogwang case STATE_READ_PROTO:
723a9643ea8Slogwang if (strcmp(token, "tcp") == 0)
724a9643ea8Slogwang proto = IPPROTO_TCP;
725a9643ea8Slogwang else if (strcmp(token, "udp") == 0)
726a9643ea8Slogwang proto = IPPROTO_UDP;
727a9643ea8Slogwang else {
728a9643ea8Slogwang ret = -1;
729a9643ea8Slogwang goto getout;
730a9643ea8Slogwang }
731a9643ea8Slogwang state = STATE_READ_KEYWORD;
732a9643ea8Slogwang break;
733a9643ea8Slogwang
734a9643ea8Slogwang case STATE_READ_SRC:
735a9643ea8Slogwang case STATE_READ_DST:
736a9643ea8Slogwang {
737a9643ea8Slogwang int err;
738a9643ea8Slogwang char *p;
739a9643ea8Slogwang struct in_addr mask;
740a9643ea8Slogwang struct in_addr addr;
741a9643ea8Slogwang
742a9643ea8Slogwang p = token;
743a9643ea8Slogwang while (*p != '/' && *p != 0)
744a9643ea8Slogwang p++;
745a9643ea8Slogwang
746a9643ea8Slogwang if (*p != '/') {
747a9643ea8Slogwang IpMask(32, &mask);
748a9643ea8Slogwang err = IpAddr(token, &addr);
749a9643ea8Slogwang if (err) {
750a9643ea8Slogwang ret = -1;
751a9643ea8Slogwang goto getout;
752a9643ea8Slogwang }
753a9643ea8Slogwang } else {
754a9643ea8Slogwang int nbits;
755a9643ea8Slogwang char s[sizeof(buffer)];
756a9643ea8Slogwang
757a9643ea8Slogwang *p = ' ';
758a9643ea8Slogwang n = sscanf(token, "%s %d", s, &nbits);
759a9643ea8Slogwang if (n != 2) {
760a9643ea8Slogwang ret = -1;
761a9643ea8Slogwang goto getout;
762a9643ea8Slogwang }
763a9643ea8Slogwang
764a9643ea8Slogwang err = IpAddr(s, &addr);
765a9643ea8Slogwang if (err) {
766a9643ea8Slogwang ret = -1;
767a9643ea8Slogwang goto getout;
768a9643ea8Slogwang }
769a9643ea8Slogwang
770a9643ea8Slogwang err = IpMask(nbits, &mask);
771a9643ea8Slogwang if (err) {
772a9643ea8Slogwang ret = -1;
773a9643ea8Slogwang goto getout;
774a9643ea8Slogwang }
775a9643ea8Slogwang }
776a9643ea8Slogwang
777a9643ea8Slogwang if (state == STATE_READ_SRC) {
778a9643ea8Slogwang src_addr = addr;
779a9643ea8Slogwang src_mask = mask;
780a9643ea8Slogwang } else {
781a9643ea8Slogwang dst_addr = addr;
782a9643ea8Slogwang dst_mask = mask;
783a9643ea8Slogwang }
784a9643ea8Slogwang }
785a9643ea8Slogwang state = STATE_READ_KEYWORD;
786a9643ea8Slogwang break;
787a9643ea8Slogwang
788a9643ea8Slogwang default:
789a9643ea8Slogwang ret = -1;
790a9643ea8Slogwang goto getout;
791a9643ea8Slogwang break;
792a9643ea8Slogwang }
793a9643ea8Slogwang
794a9643ea8Slogwang do {
795a9643ea8Slogwang token = strsep(&res, " \t");
796a9643ea8Slogwang } while (token != NULL && !*token);
797a9643ea8Slogwang }
798a9643ea8Slogwang #undef STATE_READ_KEYWORD
799a9643ea8Slogwang #undef STATE_READ_TYPE
800a9643ea8Slogwang #undef STATE_READ_PORT
801a9643ea8Slogwang #undef STATE_READ_SERVER
802a9643ea8Slogwang #undef STATE_READ_RULE
803a9643ea8Slogwang #undef STATE_READ_DELETE
804a9643ea8Slogwang #undef STATE_READ_PROTO
805a9643ea8Slogwang #undef STATE_READ_SRC
806a9643ea8Slogwang #undef STATE_READ_DST
807a9643ea8Slogwang
808a9643ea8Slogwang /* Convert port strings to numbers. This needs to be done after
809a9643ea8Slogwang the string is parsed, because the prototype might not be designated
810a9643ea8Slogwang before the ports (which might be symbolic entries in /etc/services) */
811a9643ea8Slogwang
812a9643ea8Slogwang if (strlen(str_port) != 0) {
813a9643ea8Slogwang int err;
814a9643ea8Slogwang
815a9643ea8Slogwang err = IpPort(str_port, proto, &proxy_port);
816a9643ea8Slogwang if (err) {
817a9643ea8Slogwang ret = -1;
818a9643ea8Slogwang goto getout;
819a9643ea8Slogwang }
820a9643ea8Slogwang } else {
821a9643ea8Slogwang proxy_port = 0;
822a9643ea8Slogwang }
823a9643ea8Slogwang
824a9643ea8Slogwang if (strlen(str_server_port) != 0) {
825a9643ea8Slogwang int err;
826a9643ea8Slogwang
827a9643ea8Slogwang err = IpPort(str_server_port, proto, &server_port);
828a9643ea8Slogwang if (err) {
829a9643ea8Slogwang ret = -1;
830a9643ea8Slogwang goto getout;
831a9643ea8Slogwang }
832a9643ea8Slogwang } else {
833a9643ea8Slogwang server_port = 0;
834a9643ea8Slogwang }
835a9643ea8Slogwang
836a9643ea8Slogwang /* Check that at least the server address has been defined */
837a9643ea8Slogwang if (server_addr.s_addr == 0) {
838a9643ea8Slogwang ret = -1;
839a9643ea8Slogwang goto getout;
840a9643ea8Slogwang }
841a9643ea8Slogwang
842a9643ea8Slogwang /* Add to linked list */
843a9643ea8Slogwang proxy_entry = malloc(sizeof(struct proxy_entry));
844a9643ea8Slogwang if (proxy_entry == NULL) {
845a9643ea8Slogwang ret = -1;
846a9643ea8Slogwang goto getout;
847a9643ea8Slogwang }
848a9643ea8Slogwang
849a9643ea8Slogwang proxy_entry->proxy_type = proxy_type;
850a9643ea8Slogwang proxy_entry->rule_index = rule_index;
851a9643ea8Slogwang proxy_entry->proto = proto;
852a9643ea8Slogwang proxy_entry->proxy_port = htons(proxy_port);
853a9643ea8Slogwang proxy_entry->server_port = htons(server_port);
854a9643ea8Slogwang proxy_entry->server_addr = server_addr;
855a9643ea8Slogwang proxy_entry->src_addr.s_addr = src_addr.s_addr & src_mask.s_addr;
856a9643ea8Slogwang proxy_entry->dst_addr.s_addr = dst_addr.s_addr & dst_mask.s_addr;
857a9643ea8Slogwang proxy_entry->src_mask = src_mask;
858a9643ea8Slogwang proxy_entry->dst_mask = dst_mask;
859a9643ea8Slogwang
860a9643ea8Slogwang RuleAdd(la, proxy_entry);
861a9643ea8Slogwang
862a9643ea8Slogwang getout:
863a9643ea8Slogwang LIBALIAS_UNLOCK(la);
864a9643ea8Slogwang return (ret);
865a9643ea8Slogwang }
866