]>
Commit | Line | Data |
---|---|---|
6a8d3f1c OZ |
1 | /* |
2 | * BIRD -- Bidirectional Forwarding Detection (BFD) | |
3 | * | |
4 | * Can be freely distributed and used under the terms of the GNU GPL. | |
5 | */ | |
bf139664 | 6 | |
6a8d3f1c | 7 | #include "bfd.h" |
bf139664 OZ |
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 | ||
0e175f9f OZ |
23 | #define BFD_BASE_LEN sizeof(struct bfd_ctl_packet) |
24 | #define BFD_MAX_LEN 64 | |
bf139664 | 25 | |
6a8d3f1c | 26 | static inline u8 bfd_pack_vdiag(u8 version, u8 diag) |
bf139664 OZ |
27 | { return (version << 5) | diag; } |
28 | ||
6a8d3f1c OZ |
29 | static inline u8 bfd_pack_flags(u8 state, u8 flags) |
30 | { return (state << 6) | flags; } | |
bf139664 OZ |
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 | ||
0e175f9f OZ |
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 | ||
bf139664 OZ |
62 | void |
63 | bfd_send_ctl(struct bfd_proto *p, struct bfd_session *s, int final) | |
64 | { | |
1ec52253 | 65 | sock *sk = s->ifa->sk; |
6a8d3f1c | 66 | struct bfd_ctl_packet *pkt = (struct bfd_ctl_packet *) sk->tbuf; |
0e175f9f | 67 | char fb[8]; |
bf139664 OZ |
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; | |
6a8d3f1c | 72 | pkt->length = BFD_BASE_LEN; |
bf139664 OZ |
73 | pkt->snd_id = htonl(s->loc_id); |
74 | pkt->rcv_id = htonl(s->rem_id); | |
6a8d3f1c OZ |
75 | pkt->des_min_tx_int = htonl(s->des_min_tx_new); |
76 | pkt->req_min_rx_int = htonl(s->req_min_rx_new); | |
bf139664 OZ |
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 | ||
6a8d3f1c | 84 | if (sk->tbuf != sk->tpos) |
0e175f9f OZ |
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)); | |
6a8d3f1c OZ |
89 | |
90 | sk_send_to(sk, pkt->length, s->addr, sk->dport); | |
bf139664 OZ |
91 | } |
92 | ||
6a8d3f1c OZ |
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) | |
bf139664 | 97 | { |
6a8d3f1c OZ |
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; | |
0e175f9f OZ |
102 | char fb[8]; |
103 | ||
104 | if ((sk->sport == BFD_CONTROL_PORT) && (sk->ttl < 255)) | |
105 | DROP("wrong TTL", sk->ttl); | |
bf139664 OZ |
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 | ||
0e175f9f OZ |
120 | if ((pkt->flags & BFD_FLAG_MULTIPOINT) || |
121 | ((pkt->flags & BFD_FLAG_POLL) && (pkt->flags & BFD_FLAG_FINAL))) | |
bf139664 OZ |
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) | |
0e175f9f | 135 | DROP("unknown session id", id); |
bf139664 OZ |
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 | ||
6a8d3f1c | 143 | s = bfd_find_session_by_addr(p, sk->faddr); |
bf139664 OZ |
144 | |
145 | /* FIXME: better session matching and message */ | |
1ec52253 | 146 | if (!s) |
6a8d3f1c | 147 | return 1; |
bf139664 OZ |
148 | } |
149 | ||
150 | /* FIXME: better authentication handling and message */ | |
151 | if (pkt->flags & BFD_FLAG_AP) | |
152 | DROP("authentication not supported", 0); | |
153 | ||
154 | ||
6a8d3f1c OZ |
155 | u32 old_tx_int = s->des_min_tx_int; |
156 | u32 old_rx_int = s->rem_min_rx_int; | |
bf139664 | 157 | |
1ec52253 | 158 | s->rem_id= ntohl(pkt->snd_id); |
bf139664 | 159 | s->rem_state = bfd_pkt_get_state(pkt); |
6a8d3f1c | 160 | s->rem_diag = bfd_pkt_get_diag(pkt); |
bf139664 OZ |
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 | ||
0e175f9f OZ |
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 | ||
6a8d3f1c | 169 | bfd_session_process_ctl(s, pkt->flags, old_tx_int, old_rx_int); |
bf139664 OZ |
170 | return 1; |
171 | ||
172 | drop: | |
0e175f9f | 173 | log(L_REMOTE "%s: Bad packet from %I - %s (%u)", p->p.name, sk->faddr, err_dsc, err_val); |
bf139664 OZ |
174 | return 1; |
175 | } | |
176 | ||
6a8d3f1c OZ |
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 | ||
bf139664 OZ |
184 | sock * |
185 | bfd_open_rx_sk(struct bfd_proto *p, int multihop) | |
186 | { | |
6a8d3f1c | 187 | sock *sk = sk_new(p->tpool); |
bf139664 OZ |
188 | sk->type = SK_UDP; |
189 | sk->sport = !multihop ? BFD_CONTROL_PORT : BFD_MULTI_CTL_PORT; | |
190 | sk->data = p; | |
191 | ||
0e175f9f | 192 | sk->rbsize = BFD_MAX_LEN; |
bf139664 OZ |
193 | sk->rx_hook = bfd_rx_hook; |
194 | sk->err_hook = bfd_err_hook; | |
6a8d3f1c OZ |
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 | |
bf139664 OZ |
204 | |
205 | if (sk_open(sk) < 0) | |
206 | goto err; | |
6a8d3f1c OZ |
207 | |
208 | sk_start(sk); | |
209 | return sk; | |
210 | ||
211 | err: | |
212 | rfree(sk); | |
213 | return NULL; | |
bf139664 OZ |
214 | } |
215 | ||
1ec52253 | 216 | sock * |
bf139664 OZ |
217 | bfd_open_tx_sk(struct bfd_proto *p, ip_addr local, struct iface *ifa) |
218 | { | |
6a8d3f1c | 219 | sock *sk = sk_new(p->tpool); |
bf139664 OZ |
220 | sk->type = SK_UDP; |
221 | sk->saddr = local; | |
6a8d3f1c OZ |
222 | sk->dport = ifa ? BFD_CONTROL_PORT : BFD_MULTI_CTL_PORT; |
223 | sk->iface = ifa; | |
bf139664 OZ |
224 | sk->data = p; |
225 | ||
0e175f9f | 226 | sk->tbsize = BFD_MAX_LEN; |
bf139664 | 227 | sk->err_hook = bfd_err_hook; |
bf139664 | 228 | |
6a8d3f1c OZ |
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; | |
234 | ||
235 | #ifdef IPV6 | |
236 | sk->flags |= SKF_V6ONLY; | |
237 | #endif | |
bf139664 OZ |
238 | |
239 | if (sk_open(sk) < 0) | |
240 | goto err; | |
241 | ||
6a8d3f1c OZ |
242 | sk_start(sk); |
243 | return sk; | |
244 | ||
245 | err: | |
246 | rfree(sk); | |
247 | return NULL; | |
bf139664 | 248 | } |