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