summaryrefslogtreecommitdiffstats
path: root/sysdep/unix/krt-set.c
blob: 8bc2df1190210e370cc94e4cc4ed700525e37b75 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
/*
 *	BIRD -- Unix Routing Table Syncing
 *
 *	(c) 1998 Martin Mares <mj@ucw.cz>
 *
 *	Can be freely distributed and used under the terms of the GNU GPL.
 */

#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <net/route.h>

#define LOCAL_DEBUG

#include "nest/bird.h"
#include "nest/iface.h"
#include "nest/route.h"
#include "nest/protocol.h"
#include "lib/unix.h"
#include "lib/krt.h"

int
krt_capable(rte *e)
{
  rta *a = e->attrs;

  return
    a->cast == RTC_UNICAST &&
    (a->dest == RTD_ROUTER
#ifndef CONFIG_AUTO_ROUTES
     || a->dest == RTD_DEVICE
#endif
#ifdef RTF_REJECT
     || a->dest == RTD_UNREACHABLE
#endif
     ) &&
    !a->tos;
}

void
krt_remove_route(rte *old)
{
  net *net = old->net;
  struct rtentry re;

  if (!krt_capable(old) || old->attrs->source == RTS_INHERIT)
    {
      DBG("krt_remove_route(ignored %I/%d)\n", net->n.prefix, net->n.pxlen);
      return;
    }
  DBG("krt_remove_route(%I/%d)\n", net->n.prefix, net->n.pxlen);
  bzero(&re, sizeof(re));
  fill_in_sockaddr((struct sockaddr_in *) &re.rt_dst, net->n.prefix, 0);
  fill_in_sockaddr((struct sockaddr_in *) &re.rt_genmask, ipa_mkmask(net->n.pxlen), 0);
  if (ioctl(if_scan_sock, SIOCDELRT, &re) < 0)
    log(L_ERR "SIOCDELRT(%I/%d): %m", net->n.prefix, net->n.pxlen);
}

void
krt_add_route(rte *new)
{
  net *net = new->net;
  struct rtentry re;
  rta *a = new->attrs;

  if (!krt_capable(new) || new->attrs->source == RTS_INHERIT)
    {
      DBG("krt_add_route(ignored %I/%d)\n", net->n.prefix, net->n.pxlen);
      return;
    }
  DBG("krt_add_route(%I/%d)\n", net->n.prefix, net->n.pxlen);
  bzero(&re, sizeof(re));
  fill_in_sockaddr((struct sockaddr_in *) &re.rt_dst, net->n.prefix, 0);
  fill_in_sockaddr((struct sockaddr_in *) &re.rt_genmask, ipa_mkmask(net->n.pxlen), 0);
  re.rt_flags = RTF_UP;
  if (net->n.pxlen == 32)
    re.rt_flags |= RTF_HOST;
  switch (a->dest)
    {
    case RTD_ROUTER:
      fill_in_sockaddr((struct sockaddr_in *) &re.rt_gateway, a->gw, 0);
      re.rt_flags |= RTF_GATEWAY;
      break;
#ifndef CONFIG_AUTO_ROUTES
    case RTD_DEVICE:
      re.rt_dev = a->iface->name;
      break;
#endif
#ifdef RTF_REJECT
    case RTD_UNREACHABLE:
      re.rt_flags |= RTF_REJECT;
      break;
#endif
    default:
      die("krt set: unknown flags, but not filtered");
    }

  if (ioctl(if_scan_sock, SIOCADDRT, &re) < 0)
    log(L_ERR "SIOCADDRT(%I/%d): %m", net->n.prefix, net->n.pxlen);
}

void
krt_set_notify(struct proto *x, net *net, rte *new, rte *old)
{
  if (x->state != PRS_UP)
    return;
  if (old)
    krt_remove_route(old);
  if (new)
    krt_add_route(new);
}

void
krt_set_preconfig(struct krt_proto *x)
{
  if (if_scan_sock < 0)
    die("krt set: missing socket");
  x->p.rt_notify = krt_set_notify;
}