Statistics
| Branch: | Revision:

iof-bird-daemon / proto / bfd / packets.c @ 48e5f32d

History | View | Annotate | Download (5.9 KB)

1
/*
2
 *        BIRD -- Bidirectional Forwarding Detection (BFD)
3
 *
4
 *        Can be freely distributed and used under the terms of the GNU GPL.
5
 */
6

    
7
#include "bfd.h"
8

    
9

    
10
struct bfd_ctl_packet
11
{
12
  u8 vdiag;                        /* version and diagnostic */
13
  u8 flags;                        /* state and flags */
14
  u8 detect_mult;
15
  u8 length;
16
  u32 snd_id;                        /* sender ID, aka 'my discriminator' */
17
  u32 rcv_id;                        /* receiver ID, aka 'your discriminator' */
18
  u32 des_min_tx_int;
19
  u32 req_min_rx_int;
20
  u32 req_min_echo_rx_int;
21
};
22

    
23
#define BFD_BASE_LEN        sizeof(struct bfd_ctl_packet)
24
#define BFD_MAX_LEN        64
25

    
26
static inline u8 bfd_pack_vdiag(u8 version, u8 diag)
27
{ return (version << 5) | diag; }
28

    
29
static inline u8 bfd_pack_flags(u8 state, u8 flags)
30
{ return (state << 6) | flags; }
31

    
32
static inline u8 bfd_pkt_get_version(struct bfd_ctl_packet *pkt)
33
{ return pkt->vdiag >> 5; }
34

    
35
static inline u8 bfd_pkt_get_diag(struct bfd_ctl_packet *pkt)
36
{ return pkt->vdiag && 0x1f; }
37

    
38

    
39
static inline u8 bfd_pkt_get_state(struct bfd_ctl_packet *pkt)
40
{ return pkt->flags >> 6; }
41

    
42
static inline void bfd_pkt_set_state(struct bfd_ctl_packet *pkt, u8 val)
43
{ pkt->flags = val << 6; }
44

    
45

    
46
char *
47
bfd_format_flags(u8 flags, char *buf)
48
{
49
  char *bp = buf;
50
  if (flags & BFD_FLAGS)        *bp++ = ' ';
51
  if (flags & BFD_FLAG_POLL)        *bp++ = 'P';
52
  if (flags & BFD_FLAG_FINAL)        *bp++ = 'F';
53
  if (flags & BFD_FLAG_CPI)        *bp++ = 'C';
54
  if (flags & BFD_FLAG_AP)        *bp++ = 'A';
55
  if (flags & BFD_FLAG_DEMAND)        *bp++ = 'D';
56
  if (flags & BFD_FLAG_MULTIPOINT) *bp++ = 'M';
57
  *bp = 0;
58

    
59
  return buf;
60
}
61

    
62
void
63
bfd_send_ctl(struct bfd_proto *p, struct bfd_session *s, int final)
64
{
65
  sock *sk = s->ifa->sk;
66
  struct bfd_ctl_packet *pkt = (struct bfd_ctl_packet *) sk->tbuf;
67
  char fb[8];
68

    
69
  pkt->vdiag = bfd_pack_vdiag(1, s->loc_diag);
70
  pkt->flags = bfd_pack_flags(s->loc_state, 0);
71
  pkt->detect_mult = s->detect_mult;
72
  pkt->length = BFD_BASE_LEN;
73
  pkt->snd_id = htonl(s->loc_id);
74
  pkt->rcv_id = htonl(s->rem_id);
75
  pkt->des_min_tx_int = htonl(s->des_min_tx_new);
76
  pkt->req_min_rx_int = htonl(s->req_min_rx_new);
77
  pkt->req_min_echo_rx_int = 0;
78

    
79
  if (final)
80
    pkt->flags |= BFD_FLAG_FINAL;
81
  else if (s->poll_active)
82
    pkt->flags |= BFD_FLAG_POLL;
83

    
84
  if (sk->tbuf != sk->tpos)
85
    log(L_WARN "%s: Old packet overwritten in TX buffer", p->p.name);
86

    
87
  TRACE(D_PACKETS, "Sending CTL to %I [%s%s]", s->addr,
88
        bfd_state_names[s->loc_state], bfd_format_flags(pkt->flags, fb));
89

    
90
  sk_send_to(sk, pkt->length, s->addr, sk->dport);
91
}
92

    
93
#define DROP(DSC,VAL) do { err_dsc = DSC; err_val = VAL; goto drop; } while(0)
94

    
95
static int
96
bfd_rx_hook(sock *sk, int len)
97
{
98
  struct bfd_proto *p =  sk->data;
99
  struct bfd_ctl_packet *pkt = (struct bfd_ctl_packet *) sk->rbuf;
100
  const char *err_dsc = NULL;
101
  uint err_val = 0;
102
  char fb[8];
103

    
104
  if ((sk->sport == BFD_CONTROL_PORT) && (sk->ttl < 255))
105
    DROP("wrong TTL", sk->ttl);
106

    
107
  if (len < BFD_BASE_LEN)
108
    DROP("too short", len);
109

    
110
  u8 version = bfd_pkt_get_version(pkt);
111
  if (version != 1)
112
    DROP("version mismatch", version);
113

    
114
  if ((pkt->length < BFD_BASE_LEN) || (pkt->length > len))
115
    DROP("length mismatch", pkt->length);
116

    
117
  if (pkt->detect_mult == 0)
118
    DROP("invalid detect mult", 0);
119

    
120
  if ((pkt->flags & BFD_FLAG_MULTIPOINT) ||
121
      ((pkt->flags & BFD_FLAG_POLL) && (pkt->flags & BFD_FLAG_FINAL)))
122
    DROP("invalid flags", pkt->flags);
123

    
124
  if (pkt->snd_id == 0)
125
    DROP("invalid my discriminator", 0);
126

    
127
  struct bfd_session *s;
128
  u32 id = ntohl(pkt->rcv_id);
129

    
130
  if (id)
131
  {
132
    s = bfd_find_session_by_id(p, id);
133

    
134
    if (!s)
135
      DROP("unknown session id", id);
136
  }
137
  else
138
  {
139
    u8 ps = bfd_pkt_get_state(pkt);
140
    if (ps > BFD_STATE_DOWN)
141
      DROP("invalid init state", ps);
142
      
143
    s = bfd_find_session_by_addr(p, sk->faddr);
144

    
145
    /* FIXME: better session matching and message */
146
    if (!s)
147
      return 1;
148
  }
149

    
150
  /* FIXME: better authentication handling and message */
151
  if (pkt->flags & BFD_FLAG_AP)
152
    DROP("authentication not supported", 0);
153

    
154

    
155
  u32 old_tx_int = s->des_min_tx_int;
156
  u32 old_rx_int = s->rem_min_rx_int;
157

    
158
  s->rem_id= ntohl(pkt->snd_id);
159
  s->rem_state = bfd_pkt_get_state(pkt);
160
  s->rem_diag = bfd_pkt_get_diag(pkt);
161
  s->rem_demand_mode = pkt->flags & BFD_FLAG_DEMAND;
162
  s->rem_min_tx_int = ntohl(pkt->des_min_tx_int);
163
  s->rem_min_rx_int = ntohl(pkt->req_min_rx_int);
164
  s->rem_detect_mult = pkt->detect_mult;
165

    
166
  TRACE(D_PACKETS, "CTL received from %I [%s%s]", sk->faddr,
167
        bfd_state_names[s->rem_state], bfd_format_flags(pkt->flags, fb));
168

    
169
  bfd_session_process_ctl(s, pkt->flags, old_tx_int, old_rx_int);
170
  return 1;
171

    
172
 drop:
173
  log(L_REMOTE "%s: Bad packet from %I - %s (%u)", p->p.name, sk->faddr, err_dsc, err_val);
174
  return 1;
175
}
176

    
177
static void
178
bfd_err_hook(sock *sk, int err)
179
{
180
  struct bfd_proto *p = sk->data;
181
  log(L_ERR "%s: Socket error: %m", p->p.name, err);
182
}
183

    
184
sock *
185
bfd_open_rx_sk(struct bfd_proto *p, int multihop)
186
{
187
  sock *sk = sk_new(p->tpool);
188
  sk->type = SK_UDP;
189
  sk->sport = !multihop ? BFD_CONTROL_PORT : BFD_MULTI_CTL_PORT;
190
  sk->data = p;
191

    
192
  sk->rbsize = BFD_MAX_LEN;
193
  sk->rx_hook = bfd_rx_hook;
194
  sk->err_hook = bfd_err_hook;
195

    
196
  /* TODO: configurable ToS and priority */
197
  sk->tos = IP_PREC_INTERNET_CONTROL;
198
  sk->priority = sk_priority_control;
199
  sk->flags = SKF_THREAD | SKF_LADDR_RX | (!multihop ? SKF_TTL_RX : 0);
200

    
201
#ifdef IPV6
202
  sk->flags |= SKF_V6ONLY;
203
#endif
204

    
205
  if (sk_open(sk) < 0)
206
    goto err;
207

    
208
  sk_start(sk);
209
  return sk;
210

    
211
 err:
212
  rfree(sk);
213
  return NULL;
214
}
215

    
216
sock *
217
bfd_open_tx_sk(struct bfd_proto *p, ip_addr local, struct iface *ifa)
218
{
219
  sock *sk = sk_new(p->tpool);
220
  sk->type = SK_UDP;
221
  sk->saddr = local;
222
  sk->dport = ifa ? BFD_CONTROL_PORT : BFD_MULTI_CTL_PORT;
223
  sk->iface = ifa;
224
  sk->data = p;
225

    
226
  sk->tbsize = BFD_MAX_LEN;
227
  sk->err_hook = bfd_err_hook;
228

    
229
  /* TODO: configurable ToS, priority and TTL security */
230
  sk->tos = IP_PREC_INTERNET_CONTROL;
231
  sk->priority = sk_priority_control;
232
  sk->ttl = ifa ? 255 : -1;
233
  sk->flags = SKF_THREAD | SKF_BIND;
234

    
235
#ifdef IPV6
236
  sk->flags |= SKF_V6ONLY;
237
#endif
238

    
239
  if (sk_open(sk) < 0)
240
    goto err;
241

    
242
  sk_start(sk);
243
  return sk;
244

    
245
 err:
246
  rfree(sk);
247
  return NULL;
248
}