xref: /freebsd/usr.sbin/ppp/nat_cmd.c (revision 5521ff5a4d1929056e7ffc982fac3341ca54df7c)
1 /*-
2  * Copyright (c) 2001 Charles Mott <cmott@scientech.com>
3  *                    Brian Somers <brian@Awfulhak.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  * $FreeBSD$
28  */
29 
30 #include <sys/param.h>
31 #include <netinet/in.h>
32 #include <arpa/inet.h>
33 #include <netdb.h>
34 #include <netinet/in_systm.h>
35 #include <netinet/in.h>
36 #include <netinet/ip.h>
37 #include <sys/un.h>
38 
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <termios.h>
43 
44 #ifdef LOCALNAT
45 #include "alias.h"
46 #else
47 #include <alias.h>
48 #endif
49 
50 #include "layer.h"
51 #include "proto.h"
52 #include "defs.h"
53 #include "command.h"
54 #include "log.h"
55 #include "nat_cmd.h"
56 #include "descriptor.h"
57 #include "prompt.h"
58 #include "timer.h"
59 #include "fsm.h"
60 #include "slcompress.h"
61 #include "throughput.h"
62 #include "iplist.h"
63 #include "mbuf.h"
64 #include "lqr.h"
65 #include "hdlc.h"
66 #include "ipcp.h"
67 #include "lcp.h"
68 #include "ccp.h"
69 #include "link.h"
70 #include "mp.h"
71 #include "filter.h"
72 #ifndef NORADIUS
73 #include "radius.h"
74 #endif
75 #include "ip.h"
76 #include "bundle.h"
77 
78 
79 #define NAT_EXTRABUF (13)
80 
81 static int StrToAddr(const char *, struct in_addr *);
82 static int StrToPortRange(const char *, u_short *, u_short *, const char *);
83 static int StrToAddrAndPort(const char *, struct in_addr *, u_short *,
84                             u_short *, const char *);
85 
86 static void
87 lowhigh(u_short *a, u_short *b)
88 {
89   if (a > b) {
90     u_short c;
91 
92     c = *b;
93     *b = *a;
94     *a = c;
95   }
96 }
97 
98 int
99 nat_RedirectPort(struct cmdargs const *arg)
100 {
101   if (!arg->bundle->NatEnabled) {
102     prompt_Printf(arg->prompt, "Alias not enabled\n");
103     return 1;
104   } else if (arg->argc == arg->argn + 3 || arg->argc == arg->argn + 4) {
105     char proto_constant;
106     const char *proto;
107     struct in_addr localaddr;
108     u_short hlocalport, llocalport;
109     struct in_addr aliasaddr;
110     u_short haliasport, laliasport;
111     struct in_addr remoteaddr;
112     u_short hremoteport, lremoteport;
113     struct alias_link *link;
114     int error;
115 
116     proto = arg->argv[arg->argn];
117     if (strcmp(proto, "tcp") == 0) {
118       proto_constant = IPPROTO_TCP;
119     } else if (strcmp(proto, "udp") == 0) {
120       proto_constant = IPPROTO_UDP;
121     } else {
122       prompt_Printf(arg->prompt, "port redirect: protocol must be"
123                     " tcp or udp\n");
124       return -1;
125     }
126 
127     error = StrToAddrAndPort(arg->argv[arg->argn+1], &localaddr, &llocalport,
128                              &hlocalport, proto);
129     if (error) {
130       prompt_Printf(arg->prompt, "nat port: error reading localaddr:port\n");
131       return -1;
132     }
133 
134     error = StrToPortRange(arg->argv[arg->argn+2], &laliasport, &haliasport,
135                            proto);
136     if (error) {
137       prompt_Printf(arg->prompt, "nat port: error reading alias port\n");
138       return -1;
139     }
140     aliasaddr.s_addr = INADDR_ANY;
141 
142     if (arg->argc == arg->argn + 4) {
143       error = StrToAddrAndPort(arg->argv[arg->argn+3], &remoteaddr,
144                                &lremoteport, &hremoteport, proto);
145       if (error) {
146         prompt_Printf(arg->prompt, "nat port: error reading "
147                       "remoteaddr:port\n");
148         return -1;
149       }
150     } else {
151       remoteaddr.s_addr = INADDR_ANY;
152       lremoteport = hremoteport = 0;
153     }
154 
155     lowhigh(&llocalport, &hlocalport);
156     lowhigh(&laliasport, &haliasport);
157     lowhigh(&lremoteport, &hremoteport);
158 
159     if (haliasport - laliasport != hlocalport - llocalport) {
160       prompt_Printf(arg->prompt, "nat port: local & alias port ranges "
161                     "are not equal\n");
162       return -1;
163     }
164 
165     if (hremoteport && hremoteport - lremoteport != hlocalport - llocalport) {
166       prompt_Printf(arg->prompt, "nat port: local & remote port ranges "
167                     "are not equal\n");
168       return -1;
169     }
170 
171     while (laliasport <= haliasport) {
172       link = PacketAliasRedirectPort(localaddr, htons(llocalport),
173 				     remoteaddr, htons(lremoteport),
174                                      aliasaddr, htons(laliasport),
175 				     proto_constant);
176 
177       if (link == NULL) {
178         prompt_Printf(arg->prompt, "nat port: %d: error %d\n", laliasport,
179                       error);
180         return 1;
181       }
182       llocalport++;
183       laliasport++;
184       if (hremoteport)
185         lremoteport++;
186     }
187 
188     return 0;
189   }
190 
191   return -1;
192 }
193 
194 
195 int
196 nat_RedirectAddr(struct cmdargs const *arg)
197 {
198   if (!arg->bundle->NatEnabled) {
199     prompt_Printf(arg->prompt, "nat not enabled\n");
200     return 1;
201   } else if (arg->argc == arg->argn+2) {
202     int error;
203     struct in_addr localaddr, aliasaddr;
204     struct alias_link *link;
205 
206     error = StrToAddr(arg->argv[arg->argn], &localaddr);
207     if (error) {
208       prompt_Printf(arg->prompt, "address redirect: invalid local address\n");
209       return 1;
210     }
211     error = StrToAddr(arg->argv[arg->argn+1], &aliasaddr);
212     if (error) {
213       prompt_Printf(arg->prompt, "address redirect: invalid alias address\n");
214       prompt_Printf(arg->prompt, "Usage: nat %s %s\n", arg->cmd->name,
215                     arg->cmd->syntax);
216       return 1;
217     }
218     link = PacketAliasRedirectAddr(localaddr, aliasaddr);
219     if (link == NULL) {
220       prompt_Printf(arg->prompt, "address redirect: packet aliasing"
221                     " engine error\n");
222       prompt_Printf(arg->prompt, "Usage: nat %s %s\n", arg->cmd->name,
223                     arg->cmd->syntax);
224     }
225   } else
226     return -1;
227 
228   return 0;
229 }
230 
231 
232 static int
233 StrToAddr(const char *str, struct in_addr *addr)
234 {
235   struct hostent *hp;
236 
237   if (inet_aton(str, addr))
238     return 0;
239 
240   hp = gethostbyname(str);
241   if (!hp) {
242     log_Printf(LogWARN, "StrToAddr: Unknown host %s.\n", str);
243     return -1;
244   }
245   *addr = *((struct in_addr *) hp->h_addr);
246   return 0;
247 }
248 
249 
250 static int
251 StrToPort(const char *str, u_short *port, const char *proto)
252 {
253   struct servent *sp;
254   char *end;
255 
256   *port = strtol(str, &end, 10);
257   if (*end != '\0') {
258     sp = getservbyname(str, proto);
259     if (sp == NULL) {
260       log_Printf(LogWARN, "StrToAddr: Unknown port or service %s/%s.\n",
261 	        str, proto);
262       return -1;
263     }
264     *port = ntohs(sp->s_port);
265   }
266 
267   return 0;
268 }
269 
270 static int
271 StrToPortRange(const char *str, u_short *low, u_short *high, const char *proto)
272 {
273   char *minus;
274   int res;
275 
276   minus = strchr(str, '-');
277   if (minus)
278     *minus = '\0';		/* Cheat the const-ness ! */
279 
280   res = StrToPort(str, low, proto);
281 
282   if (minus)
283     *minus = '-';		/* Cheat the const-ness ! */
284 
285   if (res == 0) {
286     if (minus)
287       res = StrToPort(minus + 1, high, proto);
288     else
289       *high = *low;
290   }
291 
292   return res;
293 }
294 
295 static int
296 StrToAddrAndPort(const char *str, struct in_addr *addr, u_short *low,
297                  u_short *high, const char *proto)
298 {
299   char *colon;
300   int res;
301 
302   colon = strchr(str, ':');
303   if (!colon) {
304     log_Printf(LogWARN, "StrToAddrAndPort: %s is missing port number.\n", str);
305     return -1;
306   }
307 
308   *colon = '\0';		/* Cheat the const-ness ! */
309   res = StrToAddr(str, addr);
310   *colon = ':';			/* Cheat the const-ness ! */
311   if (res != 0)
312     return -1;
313 
314   return StrToPortRange(colon + 1, low, high, proto);
315 }
316 
317 int
318 nat_ProxyRule(struct cmdargs const *arg)
319 {
320   char cmd[LINE_LEN];
321   int f, pos;
322   size_t len;
323 
324   if (arg->argn >= arg->argc)
325     return -1;
326 
327   for (f = arg->argn, pos = 0; f < arg->argc; f++) {
328     len = strlen(arg->argv[f]);
329     if (sizeof cmd - pos < len + (len ? 1 : 0))
330       break;
331     if (len)
332       cmd[pos++] = ' ';
333     strcpy(cmd + pos, arg->argv[f]);
334     pos += len;
335   }
336 
337   return PacketAliasProxyRule(cmd);
338 }
339 
340 int
341 nat_SetTarget(struct cmdargs const *arg)
342 {
343   struct in_addr addr;
344 
345   if (arg->argc == arg->argn) {
346     addr.s_addr = INADDR_ANY;
347     PacketAliasSetTarget(addr);
348     return 0;
349   }
350 
351   if (arg->argc != arg->argn + 1)
352     return -1;
353 
354   if (!strcasecmp(arg->argv[arg->argn], "MYADDR")) {
355     addr.s_addr = INADDR_ANY;
356     PacketAliasSetTarget(addr);
357     return 0;
358   }
359 
360   addr = GetIpAddr(arg->argv[arg->argn]);
361   if (addr.s_addr == INADDR_NONE) {
362     log_Printf(LogWARN, "%s: invalid address\n", arg->argv[arg->argn]);
363     return 1;
364   }
365 
366   PacketAliasSetTarget(addr);
367   return 0;
368 }
369 
370 static struct mbuf *
371 nat_LayerPush(struct bundle *bundle, struct link *l, struct mbuf *bp,
372                 int pri, u_short *proto)
373 {
374   if (!bundle->NatEnabled || *proto != PROTO_IP)
375     return bp;
376 
377   log_Printf(LogDEBUG, "nat_LayerPush: PROTO_IP -> PROTO_IP\n");
378   m_settype(bp, MB_NATOUT);
379   /* Ensure there's a bit of extra buffer for the NAT code... */
380   bp = m_pullup(m_append(bp, NULL, NAT_EXTRABUF));
381   PacketAliasOut(MBUF_CTOP(bp), bp->m_len);
382   bp->m_len = ntohs(((struct ip *)MBUF_CTOP(bp))->ip_len);
383 
384   return bp;
385 }
386 
387 static struct mbuf *
388 nat_LayerPull(struct bundle *bundle, struct link *l, struct mbuf *bp,
389                 u_short *proto)
390 {
391   static int gfrags;
392   int ret, len, nfrags;
393   struct mbuf **last;
394   char *fptr;
395 
396   if (!bundle->NatEnabled || *proto != PROTO_IP)
397     return bp;
398 
399   log_Printf(LogDEBUG, "nat_LayerPull: PROTO_IP -> PROTO_IP\n");
400   m_settype(bp, MB_NATIN);
401   /* Ensure there's a bit of extra buffer for the NAT code... */
402   bp = m_pullup(m_append(bp, NULL, NAT_EXTRABUF));
403   ret = PacketAliasIn(MBUF_CTOP(bp), bp->m_len);
404 
405   bp->m_len = ntohs(((struct ip *)MBUF_CTOP(bp))->ip_len);
406   if (bp->m_len > MAX_MRU) {
407     log_Printf(LogWARN, "nat_LayerPull: Problem with IP header length (%lu)\n",
408                (unsigned long)bp->m_len);
409     m_freem(bp);
410     return NULL;
411   }
412 
413   switch (ret) {
414     case PKT_ALIAS_OK:
415       break;
416 
417     case PKT_ALIAS_UNRESOLVED_FRAGMENT:
418       /* Save the data for later */
419       fptr = malloc(bp->m_len);
420       bp = mbuf_Read(bp, fptr, bp->m_len);
421       PacketAliasSaveFragment(fptr);
422       log_Printf(LogDEBUG, "Store another frag (%lu) - now %d\n",
423                  (unsigned long)((struct ip *)fptr)->ip_id, ++gfrags);
424       break;
425 
426     case PKT_ALIAS_FOUND_HEADER_FRAGMENT:
427       /* Fetch all the saved fragments and chain them on the end of `bp' */
428       last = &bp->m_nextpkt;
429       nfrags = 0;
430       while ((fptr = PacketAliasGetFragment(MBUF_CTOP(bp))) != NULL) {
431         nfrags++;
432         PacketAliasFragmentIn(MBUF_CTOP(bp), fptr);
433         len = ntohs(((struct ip *)fptr)->ip_len);
434         *last = m_get(len, MB_NATIN);
435         memcpy(MBUF_CTOP(*last), fptr, len);
436         free(fptr);
437         last = &(*last)->m_nextpkt;
438       }
439       gfrags -= nfrags;
440       log_Printf(LogDEBUG, "Found a frag header (%lu) - plus %d more frags (no"
441                  "w %d)\n", (unsigned long)((struct ip *)MBUF_CTOP(bp))->ip_id,
442                  nfrags, gfrags);
443       break;
444 
445     case PKT_ALIAS_IGNORED:
446       if (PacketAliasSetMode(0, 0) & PKT_ALIAS_DENY_INCOMING) {
447         log_Printf(LogTCPIP, "NAT engine denied data:\n");
448         m_freem(bp);
449         bp = NULL;
450       } else if (log_IsKept(LogTCPIP)) {
451         log_Printf(LogTCPIP, "NAT engine ignored data:\n");
452         PacketCheck(bundle, MBUF_CTOP(bp), bp->m_len, NULL, NULL, NULL);
453       }
454       break;
455 
456     default:
457       log_Printf(LogWARN, "nat_LayerPull: Dropped a packet (%d)....\n", ret);
458       m_freem(bp);
459       bp = NULL;
460       break;
461   }
462 
463   return bp;
464 }
465 
466 struct layer natlayer =
467   { LAYER_NAT, "nat", nat_LayerPush, nat_LayerPull };
468