nat_cmd.c revision 81033
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: head/usr.sbin/ppp/nat_cmd.c 81033 2001-08-02 10:16:32Z brian $
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
81static int StrToAddr(const char *, struct in_addr *);
82static int StrToPortRange(const char *, u_short *, u_short *, const char *);
83static int StrToAddrAndPort(const char *, struct in_addr *, u_short *,
84                            u_short *, const char *);
85
86static void
87lowhigh(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
98int
99nat_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
195int
196nat_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
232int
233nat_RedirectProto(struct cmdargs const *arg)
234{
235  if (!arg->bundle->NatEnabled) {
236    prompt_Printf(arg->prompt, "nat not enabled\n");
237    return 1;
238  } else if (arg->argc >= arg->argn + 2 && arg->argc <= arg->argn + 4) {
239    struct in_addr localIP, publicIP, remoteIP;
240    struct alias_link *link;
241    struct protoent *pe;
242    int error, len;
243
244    len = strlen(arg->argv[arg->argn]);
245    if (len == 0) {
246      prompt_Printf(arg->prompt, "proto redirect: invalid protocol\n");
247      return 1;
248    }
249    if (strspn(arg->argv[arg->argn], "01234567") == len)
250      pe = getprotobynumber(atoi(arg->argv[arg->argn]));
251    else
252      pe = getprotobyname(arg->argv[arg->argn]);
253    if (pe == NULL) {
254      prompt_Printf(arg->prompt, "proto redirect: invalid protocol\n");
255      return 1;
256    }
257
258    error = StrToAddr(arg->argv[arg->argn + 1], &localIP);
259    if (error) {
260      prompt_Printf(arg->prompt, "proto redirect: invalid src address\n");
261      return 1;
262    }
263
264    if (arg->argc >= arg->argn + 3) {
265      error = StrToAddr(arg->argv[arg->argn + 2], &publicIP);
266      if (error) {
267        prompt_Printf(arg->prompt, "proto redirect: invalid alias address\n");
268        prompt_Printf(arg->prompt, "Usage: nat %s %s\n", arg->cmd->name,
269                      arg->cmd->syntax);
270        return 1;
271      }
272    } else
273      publicIP.s_addr = INADDR_ANY;
274
275    if (arg->argc == arg->argn + 4) {
276      error = StrToAddr(arg->argv[arg->argn + 2], &remoteIP);
277      if (error) {
278        prompt_Printf(arg->prompt, "proto redirect: invalid dst address\n");
279        prompt_Printf(arg->prompt, "Usage: nat %s %s\n", arg->cmd->name,
280                      arg->cmd->syntax);
281        return 1;
282      }
283    } else
284      remoteIP.s_addr = INADDR_ANY;
285
286    link = PacketAliasRedirectProto(localIP, remoteIP, publicIP, pe->p_proto);
287    if (link == NULL) {
288      prompt_Printf(arg->prompt, "proto redirect: packet aliasing"
289                    " engine error\n");
290      prompt_Printf(arg->prompt, "Usage: nat %s %s\n", arg->cmd->name,
291                    arg->cmd->syntax);
292    }
293  } else
294    return -1;
295
296  return 0;
297}
298
299
300static int
301StrToAddr(const char *str, struct in_addr *addr)
302{
303  struct hostent *hp;
304
305  if (inet_aton(str, addr))
306    return 0;
307
308  hp = gethostbyname(str);
309  if (!hp) {
310    log_Printf(LogWARN, "StrToAddr: Unknown host %s.\n", str);
311    return -1;
312  }
313  *addr = *((struct in_addr *) hp->h_addr);
314  return 0;
315}
316
317
318static int
319StrToPort(const char *str, u_short *port, const char *proto)
320{
321  struct servent *sp;
322  char *end;
323
324  *port = strtol(str, &end, 10);
325  if (*end != '\0') {
326    sp = getservbyname(str, proto);
327    if (sp == NULL) {
328      log_Printf(LogWARN, "StrToAddr: Unknown port or service %s/%s.\n",
329	        str, proto);
330      return -1;
331    }
332    *port = ntohs(sp->s_port);
333  }
334
335  return 0;
336}
337
338static int
339StrToPortRange(const char *str, u_short *low, u_short *high, const char *proto)
340{
341  char *minus;
342  int res;
343
344  minus = strchr(str, '-');
345  if (minus)
346    *minus = '\0';		/* Cheat the const-ness ! */
347
348  res = StrToPort(str, low, proto);
349
350  if (minus)
351    *minus = '-';		/* Cheat the const-ness ! */
352
353  if (res == 0) {
354    if (minus)
355      res = StrToPort(minus + 1, high, proto);
356    else
357      *high = *low;
358  }
359
360  return res;
361}
362
363static int
364StrToAddrAndPort(const char *str, struct in_addr *addr, u_short *low,
365                 u_short *high, const char *proto)
366{
367  char *colon;
368  int res;
369
370  colon = strchr(str, ':');
371  if (!colon) {
372    log_Printf(LogWARN, "StrToAddrAndPort: %s is missing port number.\n", str);
373    return -1;
374  }
375
376  *colon = '\0';		/* Cheat the const-ness ! */
377  res = StrToAddr(str, addr);
378  *colon = ':';			/* Cheat the const-ness ! */
379  if (res != 0)
380    return -1;
381
382  return StrToPortRange(colon + 1, low, high, proto);
383}
384
385int
386nat_ProxyRule(struct cmdargs const *arg)
387{
388  char cmd[LINE_LEN];
389  int f, pos;
390  size_t len;
391
392  if (arg->argn >= arg->argc)
393    return -1;
394
395  for (f = arg->argn, pos = 0; f < arg->argc; f++) {
396    len = strlen(arg->argv[f]);
397    if (sizeof cmd - pos < len + (len ? 1 : 0))
398      break;
399    if (len)
400      cmd[pos++] = ' ';
401    strcpy(cmd + pos, arg->argv[f]);
402    pos += len;
403  }
404
405  return PacketAliasProxyRule(cmd);
406}
407
408int
409nat_SetTarget(struct cmdargs const *arg)
410{
411  struct in_addr addr;
412
413  if (arg->argc == arg->argn) {
414    addr.s_addr = INADDR_ANY;
415    PacketAliasSetTarget(addr);
416    return 0;
417  }
418
419  if (arg->argc != arg->argn + 1)
420    return -1;
421
422  if (!strcasecmp(arg->argv[arg->argn], "MYADDR")) {
423    addr.s_addr = INADDR_ANY;
424    PacketAliasSetTarget(addr);
425    return 0;
426  }
427
428  addr = GetIpAddr(arg->argv[arg->argn]);
429  if (addr.s_addr == INADDR_NONE) {
430    log_Printf(LogWARN, "%s: invalid address\n", arg->argv[arg->argn]);
431    return 1;
432  }
433
434  PacketAliasSetTarget(addr);
435  return 0;
436}
437
438#ifndef NO_FW_PUNCH
439int
440nat_PunchFW(struct cmdargs const *arg)
441{
442  char *end;
443  long base, count;
444
445  if (arg->argc == arg->argn) {
446    PacketAliasSetMode(0, PKT_ALIAS_PUNCH_FW);
447    return 0;
448  }
449
450  if (arg->argc != arg->argn + 2)
451    return -1;
452
453  base = strtol(arg->argv[arg->argn], &end, 10);
454  if (*end != '\0' || base < 0)
455    return -1;
456
457  count = strtol(arg->argv[arg->argn + 1], &end, 10);
458  if (*end != '\0' || count < 0)
459    return -1;
460
461  PacketAliasSetFWBase(base, count);
462  PacketAliasSetMode(PKT_ALIAS_PUNCH_FW, PKT_ALIAS_PUNCH_FW);
463
464  return 0;
465}
466#endif
467
468static struct mbuf *
469nat_LayerPush(struct bundle *bundle, struct link *l, struct mbuf *bp,
470                int pri, u_short *proto)
471{
472  if (!bundle->NatEnabled || *proto != PROTO_IP)
473    return bp;
474
475  log_Printf(LogDEBUG, "nat_LayerPush: PROTO_IP -> PROTO_IP\n");
476  m_settype(bp, MB_NATOUT);
477  /* Ensure there's a bit of extra buffer for the NAT code... */
478  bp = m_pullup(m_append(bp, NULL, NAT_EXTRABUF));
479  PacketAliasOut(MBUF_CTOP(bp), bp->m_len);
480  bp->m_len = ntohs(((struct ip *)MBUF_CTOP(bp))->ip_len);
481
482  return bp;
483}
484
485static struct mbuf *
486nat_LayerPull(struct bundle *bundle, struct link *l, struct mbuf *bp,
487                u_short *proto)
488{
489  static int gfrags;
490  int ret, len, nfrags;
491  struct mbuf **last;
492  char *fptr;
493
494  if (!bundle->NatEnabled || *proto != PROTO_IP)
495    return bp;
496
497  log_Printf(LogDEBUG, "nat_LayerPull: PROTO_IP -> PROTO_IP\n");
498  m_settype(bp, MB_NATIN);
499  /* Ensure there's a bit of extra buffer for the NAT code... */
500  bp = m_pullup(m_append(bp, NULL, NAT_EXTRABUF));
501  ret = PacketAliasIn(MBUF_CTOP(bp), bp->m_len);
502
503  bp->m_len = ntohs(((struct ip *)MBUF_CTOP(bp))->ip_len);
504  if (bp->m_len > MAX_MRU) {
505    log_Printf(LogWARN, "nat_LayerPull: Problem with IP header length (%lu)\n",
506               (unsigned long)bp->m_len);
507    m_freem(bp);
508    return NULL;
509  }
510
511  switch (ret) {
512    case PKT_ALIAS_OK:
513      break;
514
515    case PKT_ALIAS_UNRESOLVED_FRAGMENT:
516      /* Save the data for later */
517      fptr = malloc(bp->m_len);
518      bp = mbuf_Read(bp, fptr, bp->m_len);
519      PacketAliasSaveFragment(fptr);
520      log_Printf(LogDEBUG, "Store another frag (%lu) - now %d\n",
521                 (unsigned long)((struct ip *)fptr)->ip_id, ++gfrags);
522      break;
523
524    case PKT_ALIAS_FOUND_HEADER_FRAGMENT:
525      /* Fetch all the saved fragments and chain them on the end of `bp' */
526      last = &bp->m_nextpkt;
527      nfrags = 0;
528      while ((fptr = PacketAliasGetFragment(MBUF_CTOP(bp))) != NULL) {
529        nfrags++;
530        PacketAliasFragmentIn(MBUF_CTOP(bp), fptr);
531        len = ntohs(((struct ip *)fptr)->ip_len);
532        *last = m_get(len, MB_NATIN);
533        memcpy(MBUF_CTOP(*last), fptr, len);
534        free(fptr);
535        last = &(*last)->m_nextpkt;
536      }
537      gfrags -= nfrags;
538      log_Printf(LogDEBUG, "Found a frag header (%lu) - plus %d more frags (no"
539                 "w %d)\n", (unsigned long)((struct ip *)MBUF_CTOP(bp))->ip_id,
540                 nfrags, gfrags);
541      break;
542
543    case PKT_ALIAS_IGNORED:
544      if (PacketAliasSetMode(0, 0) & PKT_ALIAS_DENY_INCOMING) {
545        log_Printf(LogTCPIP, "NAT engine denied data:\n");
546        m_freem(bp);
547        bp = NULL;
548      } else if (log_IsKept(LogTCPIP)) {
549        log_Printf(LogTCPIP, "NAT engine ignored data:\n");
550        PacketCheck(bundle, MBUF_CTOP(bp), bp->m_len, NULL, NULL, NULL);
551      }
552      break;
553
554    default:
555      log_Printf(LogWARN, "nat_LayerPull: Dropped a packet (%d)....\n", ret);
556      m_freem(bp);
557      bp = NULL;
558      break;
559  }
560
561  return bp;
562}
563
564struct layer natlayer =
565  { LAYER_NAT, "nat", nat_LayerPush, nat_LayerPull };
566