nat_cmd.c revision 58285
1/*-
2 * The code in this file was written by Eivind Eklund <perhaps@yes.no>,
3 * who places it in the public domain without restriction.
4 *
5 * $FreeBSD: head/usr.sbin/ppp/nat_cmd.c 58285 2000-03-19 12:37:27Z brian $
6 */
7
8#include <sys/param.h>
9#include <netinet/in.h>
10#include <arpa/inet.h>
11#include <netdb.h>
12#include <netinet/in_systm.h>
13#include <netinet/in.h>
14#include <netinet/ip.h>
15#include <sys/un.h>
16
17#include <stdio.h>
18#include <stdlib.h>
19#include <string.h>
20#include <termios.h>
21
22#ifdef LOCALNAT
23#include "alias.h"
24#else
25#include <alias.h>
26#endif
27
28#include "layer.h"
29#include "proto.h"
30#include "defs.h"
31#include "command.h"
32#include "log.h"
33#include "nat_cmd.h"
34#include "descriptor.h"
35#include "prompt.h"
36#include "timer.h"
37#include "fsm.h"
38#include "slcompress.h"
39#include "throughput.h"
40#include "iplist.h"
41#include "mbuf.h"
42#include "lqr.h"
43#include "hdlc.h"
44#include "ipcp.h"
45#include "lcp.h"
46#include "ccp.h"
47#include "link.h"
48#include "mp.h"
49#include "filter.h"
50#ifndef NORADIUS
51#include "radius.h"
52#endif
53#include "bundle.h"
54
55
56#define NAT_EXTRABUF (13)
57
58static int StrToAddr(const char *, struct in_addr *);
59static int StrToPortRange(const char *, u_short *, u_short *, const char *);
60static int StrToAddrAndPort(const char *, struct in_addr *, u_short *,
61                            u_short *, const char *);
62
63static void
64lowhigh(u_short *a, u_short *b)
65{
66  if (a > b) {
67    u_short c;
68
69    c = *b;
70    *b = *a;
71    *a = c;
72  }
73}
74
75int
76nat_RedirectPort(struct cmdargs const *arg)
77{
78  if (!arg->bundle->NatEnabled) {
79    prompt_Printf(arg->prompt, "Alias not enabled\n");
80    return 1;
81  } else if (arg->argc == arg->argn + 3 || arg->argc == arg->argn + 4) {
82    char proto_constant;
83    const char *proto;
84    struct in_addr localaddr;
85    u_short hlocalport, llocalport;
86    struct in_addr aliasaddr;
87    u_short haliasport, laliasport;
88    struct in_addr remoteaddr;
89    u_short hremoteport, lremoteport;
90    struct alias_link *link;
91    int error;
92
93    proto = arg->argv[arg->argn];
94    if (strcmp(proto, "tcp") == 0) {
95      proto_constant = IPPROTO_TCP;
96    } else if (strcmp(proto, "udp") == 0) {
97      proto_constant = IPPROTO_UDP;
98    } else {
99      prompt_Printf(arg->prompt, "port redirect: protocol must be"
100                    " tcp or udp\n");
101      return -1;
102    }
103
104    error = StrToAddrAndPort(arg->argv[arg->argn+1], &localaddr, &llocalport,
105                             &hlocalport, proto);
106    if (error) {
107      prompt_Printf(arg->prompt, "nat port: error reading localaddr:port\n");
108      return -1;
109    }
110
111    error = StrToPortRange(arg->argv[arg->argn+2], &laliasport, &haliasport,
112                           proto);
113    if (error) {
114      prompt_Printf(arg->prompt, "nat port: error reading alias port\n");
115      return -1;
116    }
117    aliasaddr.s_addr = INADDR_ANY;
118
119    if (arg->argc == arg->argn + 4) {
120      error = StrToAddrAndPort(arg->argv[arg->argn+3], &remoteaddr,
121                               &lremoteport, &hremoteport, proto);
122      if (error) {
123        prompt_Printf(arg->prompt, "nat port: error reading "
124                      "remoteaddr:port\n");
125        return -1;
126      }
127    } else {
128      remoteaddr.s_addr = INADDR_ANY;
129      lremoteport = hremoteport = 0;
130    }
131
132    lowhigh(&llocalport, &hlocalport);
133    lowhigh(&laliasport, &haliasport);
134    lowhigh(&lremoteport, &hremoteport);
135
136    if (haliasport - laliasport != hlocalport - llocalport) {
137      prompt_Printf(arg->prompt, "nat port: local & alias port ranges "
138                    "are not equal\n");
139      return -1;
140    }
141
142    if (hremoteport && hremoteport - lremoteport != hlocalport - llocalport) {
143      prompt_Printf(arg->prompt, "nat port: local & remote port ranges "
144                    "are not equal\n");
145      return -1;
146    }
147
148    while (laliasport <= haliasport) {
149      link = PacketAliasRedirectPort(localaddr, htons(llocalport),
150				     remoteaddr, htons(lremoteport),
151                                     aliasaddr, htons(laliasport),
152				     proto_constant);
153
154      if (link == NULL) {
155        prompt_Printf(arg->prompt, "nat port: %d: error %d\n", laliasport,
156                      error);
157        return 1;
158      }
159      llocalport++;
160      laliasport++;
161      if (hremoteport)
162        lremoteport++;
163    }
164
165    return 0;
166  }
167
168  return -1;
169}
170
171
172int
173nat_RedirectAddr(struct cmdargs const *arg)
174{
175  if (!arg->bundle->NatEnabled) {
176    prompt_Printf(arg->prompt, "nat not enabled\n");
177    return 1;
178  } else if (arg->argc == arg->argn+2) {
179    int error;
180    struct in_addr localaddr, aliasaddr;
181    struct alias_link *link;
182
183    error = StrToAddr(arg->argv[arg->argn], &localaddr);
184    if (error) {
185      prompt_Printf(arg->prompt, "address redirect: invalid local address\n");
186      return 1;
187    }
188    error = StrToAddr(arg->argv[arg->argn+1], &aliasaddr);
189    if (error) {
190      prompt_Printf(arg->prompt, "address redirect: invalid alias address\n");
191      prompt_Printf(arg->prompt, "Usage: nat %s %s\n", arg->cmd->name,
192                    arg->cmd->syntax);
193      return 1;
194    }
195    link = PacketAliasRedirectAddr(localaddr, aliasaddr);
196    if (link == NULL) {
197      prompt_Printf(arg->prompt, "address redirect: packet aliasing"
198                    " engine error\n");
199      prompt_Printf(arg->prompt, "Usage: nat %s %s\n", arg->cmd->name,
200                    arg->cmd->syntax);
201    }
202  } else
203    return -1;
204
205  return 0;
206}
207
208
209static int
210StrToAddr(const char *str, struct in_addr *addr)
211{
212  struct hostent *hp;
213
214  if (inet_aton(str, addr))
215    return 0;
216
217  hp = gethostbyname(str);
218  if (!hp) {
219    log_Printf(LogWARN, "StrToAddr: Unknown host %s.\n", str);
220    return -1;
221  }
222  *addr = *((struct in_addr *) hp->h_addr);
223  return 0;
224}
225
226
227static int
228StrToPort(const char *str, u_short *port, const char *proto)
229{
230  struct servent *sp;
231  char *end;
232
233  *port = strtol(str, &end, 10);
234  if (*end != '\0') {
235    sp = getservbyname(str, proto);
236    if (sp == NULL) {
237      log_Printf(LogWARN, "StrToAddr: Unknown port or service %s/%s.\n",
238	        str, proto);
239      return -1;
240    }
241    *port = ntohs(sp->s_port);
242  }
243
244  return 0;
245}
246
247static int
248StrToPortRange(const char *str, u_short *low, u_short *high, const char *proto)
249{
250  char *minus;
251  int res;
252
253  minus = strchr(str, '-');
254  if (minus)
255    *minus = '\0';		/* Cheat the const-ness ! */
256
257  res = StrToPort(str, low, proto);
258
259  if (minus)
260    *minus = '-';		/* Cheat the const-ness ! */
261
262  if (res == 0) {
263    if (minus)
264      res = StrToPort(minus + 1, high, proto);
265    else
266      *high = *low;
267  }
268
269  return res;
270}
271
272static int
273StrToAddrAndPort(const char *str, struct in_addr *addr, u_short *low,
274                 u_short *high, const char *proto)
275{
276  char *colon;
277  int res;
278
279  colon = strchr(str, ':');
280  if (!colon) {
281    log_Printf(LogWARN, "StrToAddrAndPort: %s is missing port number.\n", str);
282    return -1;
283  }
284
285  *colon = '\0';		/* Cheat the const-ness ! */
286  res = StrToAddr(str, addr);
287  *colon = ':';			/* Cheat the const-ness ! */
288  if (res != 0)
289    return -1;
290
291  return StrToPortRange(colon + 1, low, high, proto);
292}
293
294int
295nat_ProxyRule(struct cmdargs const *arg)
296{
297  char cmd[LINE_LEN];
298  int f, pos;
299  size_t len;
300
301  if (arg->argn >= arg->argc)
302    return -1;
303
304  for (f = arg->argn, pos = 0; f < arg->argc; f++) {
305    len = strlen(arg->argv[f]);
306    if (sizeof cmd - pos < len + (f ? 1 : 0))
307      break;
308    if (f)
309      cmd[pos++] = ' ';
310    strcpy(cmd + pos, arg->argv[f]);
311    pos += len;
312  }
313
314  return PacketAliasProxyRule(cmd);
315}
316
317int
318nat_Pptp(struct cmdargs const *arg)
319{
320  struct in_addr addr;
321
322  if (arg->argc == arg->argn) {
323    addr.s_addr = INADDR_NONE;
324    PacketAliasPptp(addr);
325    return 0;
326  }
327
328  if (arg->argc != arg->argn + 1)
329    return -1;
330
331  addr = GetIpAddr(arg->argv[arg->argn]);
332  if (addr.s_addr == INADDR_NONE) {
333    log_Printf(LogWARN, "%s: invalid address\n", arg->argv[arg->argn]);
334    return 1;
335  }
336
337  PacketAliasPptp(addr);
338  return 0;
339}
340
341static struct mbuf *
342nat_LayerPush(struct bundle *bundle, struct link *l, struct mbuf *bp,
343                int pri, u_short *proto)
344{
345  if (!bundle->NatEnabled || *proto != PROTO_IP)
346    return bp;
347
348  log_Printf(LogDEBUG, "nat_LayerPush: PROTO_IP -> PROTO_IP\n");
349  m_settype(bp, MB_NATOUT);
350  /* Ensure there's a bit of extra buffer for the NAT code... */
351  bp = m_pullup(m_append(bp, NULL, NAT_EXTRABUF));
352  PacketAliasOut(MBUF_CTOP(bp), bp->m_len);
353  bp->m_len = ntohs(((struct ip *)MBUF_CTOP(bp))->ip_len);
354
355  return bp;
356}
357
358static struct mbuf *
359nat_LayerPull(struct bundle *bundle, struct link *l, struct mbuf *bp,
360                u_short *proto)
361{
362  static int gfrags;
363  struct ip *pip, *piip;
364  int ret, len, nfrags;
365  struct mbuf **last;
366  char *fptr;
367
368  if (!bundle->NatEnabled || *proto != PROTO_IP)
369    return bp;
370
371  log_Printf(LogDEBUG, "nat_LayerPull: PROTO_IP -> PROTO_IP\n");
372  m_settype(bp, MB_NATIN);
373  bp = m_pullup(bp);
374  pip = (struct ip *)MBUF_CTOP(bp);
375  piip = (struct ip *)((char *)pip + (pip->ip_hl << 2));
376
377  if (pip->ip_p == IPPROTO_IGMP ||
378      (pip->ip_p == IPPROTO_IPIP && IN_CLASSD(ntohl(piip->ip_dst.s_addr))))
379    return bp;
380
381  /* Ensure there's a bit of extra buffer for the NAT code... */
382  bp = m_pullup(m_append(bp, NULL, NAT_EXTRABUF));
383  ret = PacketAliasIn(MBUF_CTOP(bp), bp->m_len);
384  pip = (struct ip *)MBUF_CTOP(bp);
385
386  bp->m_len = ntohs(pip->ip_len);
387  if (bp->m_len > MAX_MRU) {
388    log_Printf(LogWARN, "nat_LayerPull: Problem with IP header length (%lu)\n",
389               (unsigned long)bp->m_len);
390    m_freem(bp);
391    return NULL;
392  }
393
394  switch (ret) {
395    case PKT_ALIAS_OK:
396      break;
397
398    case PKT_ALIAS_UNRESOLVED_FRAGMENT:
399      /* Save the data for later */
400      fptr = malloc(bp->m_len);
401      bp = mbuf_Read(bp, fptr, bp->m_len);
402      PacketAliasSaveFragment(fptr);
403      log_Printf(LogDEBUG, "Store another frag (%lu) - now %d\n",
404                 (unsigned long)((struct ip *)fptr)->ip_id, ++gfrags);
405      break;
406
407    case PKT_ALIAS_FOUND_HEADER_FRAGMENT:
408      /* Fetch all the saved fragments and chain them on the end of `bp' */
409      last = &bp->m_nextpkt;
410      nfrags = 0;
411      while ((fptr = PacketAliasGetFragment(MBUF_CTOP(bp))) != NULL) {
412        nfrags++;
413        PacketAliasFragmentIn(MBUF_CTOP(bp), fptr);
414        len = ntohs(((struct ip *)fptr)->ip_len);
415        *last = m_get(len, MB_NATIN);
416        memcpy(MBUF_CTOP(*last), fptr, len);
417        free(fptr);
418        last = &(*last)->m_nextpkt;
419      }
420      gfrags -= nfrags;
421      log_Printf(LogDEBUG, "Found a frag header (%lu) - plus %d more frags (no"
422                 "w %d)\n", (unsigned long)((struct ip *)MBUF_CTOP(bp))->ip_id,
423                 nfrags, gfrags);
424      break;
425
426    default:
427      log_Printf(LogWARN, "nat_LayerPull: Dropped a packet....\n");
428      m_freem(bp);
429      bp = NULL;
430      break;
431  }
432
433  return bp;
434}
435
436struct layer natlayer =
437  { LAYER_NAT, "nat", nat_LayerPush, nat_LayerPull };
438