summaryrefslogtreecommitdiff
path: root/cleopatre/application/igmp_snoopd/inc/internal.h
blob: e59fd5168f448e5d6dff0f9497433721ac84e497 (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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
#ifndef INTERNAL_H
#define INTERNAL_H

/*
 * igmp_snoopd: an IGMP Snooping Daemon.
 * Copyright (C) 2011 SPiDCOM Technologies
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include <net/ethernet.h>
#include <linux/igmp.h>
#include <stdbool.h>

#include "libspid.h"

#include "data_struct.h"
#include "utils.h"

/* Some default values from the RFCs */
#define DEFAULT_ROBUSTNESS_VARIABLE     2
#define DEFAULT_QUERY_INTERVAL      125 /* seconds */
#define DEFAULT_QUERY_RESPONSE_INTERVAL     10 /* seconds */

/* When no querier is present, this is the maximum duration after which
 * a group or a member is considered inactive. */
/* TODO: choose correct value */
/* TODO: move to config file */
#define NO_QUERIER_GROUP_MEMBERSHIP_INTERVAL     86400  /* seconds,
                                             ie: 24 hours (arbitrary value). */

/* Value of [Older Host Present Interval] to use when no querier is present. */
/* TODO: move to config file */
#define NO_QUERIER_OLDER_HOST_PRESENT_INTERVAL      86400  /* seconds,
                                             ie: 24 hours (arbitrary value). */
/* When no querier is present, this is the interval between membership timeout
 * checks. */
/* TODO: move to config file */
#define NO_QUERIER_MEMBERSHIP_TIMEOUT_CHECK_INTERVAL    (86400) /* seconds */

#define MSG_TOO_SMALL(start, size, end) ((unsigned char *)(start) + (size) > (end))

/* Because it's a multicast address, it starts with 01-00-05,
 * so only the 3 last octets need to be compared */
#define CMP_MCAST_ETH_ADDR(eth_addr1, eth_addr2) \
            (memcmp(&(eth_addr1)[3], &(eth_addr2)[3], 3))

#define IGMP_V3_GENERAL_QUERY_LEN  (sizeof (struct igmpv3_query))

/** The type of traffic. */
typedef enum
{
    /** Untagged (non VLAN) frames. */
    TRAFFIC_TYPE_UNTAGGED,

    /** VLAN  (802.1Q) tagged frames. */
    TRAFFIC_TYPE_VLAN_TAGGED

} traffic_type_t;

/** Group compatibility mode. */
typedef enum
{
    GROUP_COMPAT_MODE_IGMP_V2,
    GROUP_COMPAT_MODE_IGMP_V3
} group_compatibility_mode_t;

/** A duration in seconds */
typedef unsigned int duration_sec_t; /* TODO: unsigned int or double ?*/

/** Whether timer_querier_presence expired or not. */
extern volatile sig_atomic_t timer_expired_querier_presence;

/** Whether timer_query_tx expired or not. */
extern volatile sig_atomic_t timer_expired_query_tx;

/** Whether timer_membership_timemout expired or not. */
extern volatile sig_atomic_t timer_expired_membership_timeout;


/** IGMP metrics */
struct igmp_metrics
{
    /** Robustess variable. See RFC.*/
    uint8_t robustness_variable;

    /** Query interval. See RFC. */
    duration_sec_t query_interval;

    /** Query response interval. See RFC. */
    duration_sec_t query_response_interval;

    /** Group Membership Interval. See RFC. */
    duration_sec_t group_membership_interval;

    /** Other Querier present Interval. See RFC. */
    duration_sec_t other_querier_present_interval;

    /** Older Host Present Interval. See RFC. */
    duration_sec_t older_host_present_interval;
};

/** The context */
struct context
{
    /** The head of the list containing the multicast groups. */
    struct list_head_groups groups_head;

    /** The IGMP metrics. */
    struct igmp_metrics metrics;

    /** The last multicast info entries written to a file. */
    libspid_multicast_info_t *multicast_info;

    /** timer for detecting that no querier is present. */
    timer_t timer_querier_presence;

    /** timer for sending queries. */
    timer_t timer_query_tx;

    /** timer for the membership timeout mechanism. */
    timer_t timer_membership_timeout;

    /** Whether a querier is present or not */
    bool is_querier_present;

    /**  Whether to send queries or not, when no querier is present */
    bool query_tx;

    /** Reference time for the current event. */
    time_t ref_time;
};


/**
 * Update the computed metrics.
 *
 * Some of the metrics are computed from the values of other metrics.
 * This function updates these computed metrics.
 *
 * \param  metrics  The metrics.
 */
void update_computed_metrics (struct igmp_metrics *metrics);

/**
 * Update some timers and timestamps in the group and member.
 * They are used to keep track of (in)activity.
 * \param  ctx  The context.
 * \param  grp  The group.
 * \param  member  The member.
 */
void
update_timers (const struct context *ctx, struct group *grp,
               struct group_member *member);

/**
 * Update timer_querier_presence.
 * \param  ctx  The context.
 */
void
update_timer_querier_presence (struct context *ctx);

/**
 * Update timer_query_tx.
 * \param  ctx  the context.
 */
void
update_timer_query_tx (struct context *ctx);

/**
 * Update timer_membership_timeout.
 * \param  ctx  The context.
 */
void
update_timer_membership_timeout (struct context *ctx);

/**
 * Initialize the metrics.
 * \param  metrics  The IGMP metrics.
 */
void init_metrics (struct igmp_metrics *metrics);

/**
 * Initialize a multicast_info structure.
 * \param  mcast_info  The structure to initialize.
 */
void init_multicast_info (libspid_multicast_info_t *mcast_info);

/**
 * Init the timers.
 * \param  ctx  The context.
 * \return  0, on success.
 *          -1, on error.
 */
int
init_timers (struct context *ctx);

/**
 * Install the timers.
 * \return  0, on success.
 *          -1, on error.
 */
int
install_timers (void);

/**
 * Reset the metrics to their default values.
 * \param  metrics  The metrics.
 */
void
reset_metrics (struct igmp_metrics *metrics);

/** Dump info about the timers.
 * \param  ctx  The context.
 */
void
dump_timers (const struct context *ctx);

/**
 * Set the timer delay.
 * \param  timer  The timer.
 * \param  delay_tv_sec  The seconds part of the delay.
 * \param  delay_tv_nsec  The nanoseconds part of the delay.
 */
void
set_timer_delay (timer_t *timer, time_t delay_tv_sec, long delay_tv_nsec);

/**
 * Set the timer end.
 * \param  timer  The timer.
 * \param  end_tv_sec  The seconds part of the end value.
 * \param  end_tv_nsec  The nanoseconds part of the end value.
 */
void
set_timer_end (timer_t *timer, time_t end_tv_sec, long end_tv_nsec);

/**
 * Is timer_querier_presence running.
 * \param  ctx  The context.
 * \return  true, if the timer is running.
 *          false, otherwise.
 */
bool
is_timer_running_querier_presence (struct context *ctx);

/**
 * Generate an IGMP v3 General Query.
 * \param  buffer  The buffer where to write the query content.
 */
void
gen_igmp_v3_general_query (unsigned char *buffer);

/**
 * Process the IGMP part of a packet.
 * \param  ctx  the context.
 * \param  eth_source_addr  The Ethernet source address.
 * \param  igmp_start  The start of the IGMP part (header + data).
 * \param  msg_end  The end of the packet.
 * \return  true, if the packet was fully processed.
 *          false, if something prevented the full processing.
 */
bool
process_igmp_packet (struct context *ctx,
                     const unsigned char eth_source_addr[ETH_ALEN],
                     unsigned char *igmp_start,
                     unsigned char *msg_end);

/**
 * Process an IGMP Query.
 * \param  ctx  The context.
 * \param  igmp_header  The IGMP Header in the message.
 * \param  msg_end  The end of the message.
 */
void
process_query (struct context *ctx, struct igmphdr *igmp_header, unsigned char *msg_end);

/**
 * Process a v1 query.
 */
void process_v1_query (void);

/**
 * Process a v1 report.
 */
void process_v1_report (void);

/**
 * Process a v2 query.
 * \param  ctx  The context.
 * \param  igmp_header  The IGMP Header of the query.
 */
void
process_v2_query (struct context *ctx, struct igmphdr *igmp_header);

/**
 * Process a v2 report.
 * \param  ctx  The context.
 * \param  host_eth_addr  The Ethernet MAC address of the host that sent the report.
 * \param  igmp_header  The IGMP Header.
 */
void process_v2_report (struct context *ctx,
                        const unsigned char host_eth_addr[ETH_ALEN],
                        struct igmphdr *igmp_header);

/**
 * Process a v2 leave report.
 * \param  ctx  The context.
 * \param  host_eth_addr  The Ethernet MAC address of the host that sent the report.
 * \param  igmp_header  The IGMP Header.
 */
void process_v2_leave (struct context *ctx,
                       const unsigned char host_eth_addr[ETH_ALEN],
                       struct igmphdr *igmp_header);

/**
 * Process a v3 query.
 * \param  ctx  The context.
 * \param  start  The start of the query.
 * \param  msg_end  The end of the message(containing the report).
 */
void process_v3_query (struct context *ctx,
                       const unsigned char *start,
                       const unsigned char *msg_end);

/**
 * Process a v3 report.
 * \param  ctx  The context.
 * \param  host_eth_addr  The Ethernet MAC address of the host that sent the report.
 * \param  start  The start of the report.
 * \param  msg_end  The end of the message(containing the report).
 */
void process_v3_report (struct context *ctx,
                        const unsigned char host_eth_addr[ETH_ALEN],
                        const unsigned char *start,
                        const unsigned char * msg_end);

/**
 * Use the membership timeout mechanism to remove inactive groups and members.
 * \param  ctx  The context.
 */
void check_membership_timeout (struct context *ctx);

/**
 * Dump the info (metrics and others).
 * \param  metrics  The IGMP metrics.
 */
void dump_info (const struct igmp_metrics *metrics);

/**
 * Update the content of the multicast info. During the process, compare
 * with previous info to determine if there is a change, so the file needs to
 * be updated.
 * \param  mcast_info  The previous multicast info that will be used to detect
 *                     changes and will eventually be updated.
 * \param  groups_head  The head of the group list.
 * \return  true, if the file needs to be updated because the multicast info
 *              changed.
 *          false, if the file doesn't need to be updated because the multicast
 *              info didn't change since the previous write.
 */
bool update_multicast_info (libspid_multicast_info_t **multicast_info,
                            const struct list_head_groups *groups_head);

/**
 * Remove a member if needed, then remove the corresponding group if needed.
 *
 * Delete a member if it is in INCLUDE{}.
 * If the group becomes empty, delete it to.
 *
 * \param  grp  The group.
 * \param  member  The member.
 */
void trim (struct group *grp, struct group_member *member);

/**
 * Read config.
 * \param[out]  dominant_traffic_type  The dominant traffic type.
 * \param[out]  query_tx  Whether to send queries or not.
 * \return  0, on success.
 *          -1, on error.
 */
int
read_config (traffic_type_t *dominant_traffic_type, bool *query_tx);

/**
 * Tell if the multicast address should be ignored by the IGMP Snooping.
 * \param  multicast_addr  The multicast address to check.
 * \return  true, if the address should be ignored. false, otherwise.
 */
static ALWAYS_INLINE bool
ignore_multicast_addr (in_addr_t multicast_addr)
{
    /* Don't track group membership for special/local multicast addresses
     * ie: 224.0.0.0/24 */
    return ((multicast_addr & IGMP_LOCAL_GROUP_MASK) == IGMP_LOCAL_GROUP);
}

/**
 * Map a (big-endian) multicast IP address to a (big-endian) multicast Ethernet MAC address.
 * \param  ip_addr  The multicast IP address (in big-endian).
 * \param  eth_addr  The mapped multicast Ethernet MAC address (in big-endian).
 */
static ALWAYS_INLINE void
mcast_ip_to_eth_addr (in_addr_t ip_addr, unsigned char *eth_addr)
{
    /* From the RFC 1112:
     * "An IP host group address is mapped to an Ethernet multicast address
     * by placing the low-order 23-bits of the IP address into the low-order
     * 23 bits of the Ethernet multicast address 01-00-5E-00-00-00 (hex).
     * Because there are 28 significant bits in an IP host group address,
     * more than one host group address may map to the same Ethernet
     * multicast address".
     * For example, both 239.1.2.3 and 239.129.2.3 map to 01:00:5e:01:02:03 */

    eth_addr[0] = 0x01;
    eth_addr[1] = 0x00;
    eth_addr[2] = 0x5e;

    uint8_t *byte = (uint8_t *)&ip_addr;

    eth_addr[3] = byte[1] & 0x7f;
    eth_addr[4] = byte[2];
    eth_addr[5] = byte[3];
}

#endif /* INTERNAL_H */