1 /*
2 pmacct (Promiscuous mode IP Accounting package)
3 pmacct is Copyright (C) 2003-2019 by Paolo Lucente
4 */
5
6 /*
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 */
21
22 /* Based on BGP standard and extended communities implementation from Quagga */
23
24 #include "pmacct.h"
25 #include "bgp_prefix.h"
26 #include "bgp.h"
27
28 /* Allocate a new lcommunities. */
29 struct lcommunity *
lcommunity_new(struct bgp_peer * peer)30 lcommunity_new (struct bgp_peer *peer)
31 {
32 struct bgp_misc_structs *bms;
33 void *tmp;
34
35 if (!peer) return NULL;
36
37 bms = bgp_select_misc_db(peer->type);
38
39 if (!bms) return NULL;
40
41 tmp = malloc(sizeof (struct lcommunity));
42 if (!tmp) {
43 Log(LOG_ERR, "ERROR ( %s/%s ): malloc() failed (lcommunity_new). Exiting ..\n", config.name, bms->log_str);
44 exit_gracefully(1);
45 }
46 memset(tmp, 0, sizeof (struct lcommunity));
47
48 return (struct lcommunity *) tmp;
49 }
50
51 /* Allocate lcommunities. */
52 void
lcommunity_free(struct lcommunity * lcom)53 lcommunity_free (struct lcommunity *lcom)
54 {
55 if (lcom->val) free(lcom->val);
56 if (lcom->str) free(lcom->str);
57 free(lcom);
58 }
59
60 /* Add a new Large Communities value to Large Communities
61 Attribute structure. When the value already exists in
62 the structure, we don't add the value. Newly added
63 value is sorted by numerical order. When the value is
64 added to the structure return 1 else return 0. */
65 static int
lcommunity_add_val(struct bgp_peer * peer,struct lcommunity * lcom,struct lcommunity_val * lval)66 lcommunity_add_val (struct bgp_peer *peer, struct lcommunity *lcom, struct lcommunity_val *lval)
67 {
68 struct bgp_misc_structs *bms;
69 u_int8_t *p;
70 int ret;
71 int c;
72
73 if (!peer) return ERR;
74
75 bms = bgp_select_misc_db(peer->type);
76
77 if (!bms) return ERR;
78
79 /* When this is fist value, just add it. */
80 if (lcom->val == NULL)
81 {
82 lcom->size++;
83 lcom->val = malloc(lcom_length (lcom));
84 if (!lcom->val) {
85 Log(LOG_ERR, "ERROR ( %s/%s ): malloc() failed (lcommunity_add_val). Exiting ..\n", config.name, bms->log_str);
86 exit_gracefully(1);
87 }
88 memcpy (lcom->val, lval->val, LCOMMUNITY_SIZE);
89 return 1;
90 }
91
92 /* If the value already exists in the structure return 0. */
93 c = 0;
94 for (p = lcom->val; c < lcom->size; p += LCOMMUNITY_SIZE, c++)
95 {
96 ret = memcmp (p, lval->val, LCOMMUNITY_SIZE);
97 if (ret == 0)
98 return 0;
99 if (ret > 0)
100 break;
101 }
102
103 /* Add the value to the structure with numerical sorting. */
104 lcom->size++;
105 lcom->val = realloc(lcom->val, lcom_length (lcom));
106
107 memmove (lcom->val + (c + 1) * LCOMMUNITY_SIZE,
108 lcom->val + c * LCOMMUNITY_SIZE,
109 (lcom->size - 1 - c) * LCOMMUNITY_SIZE);
110 memcpy (lcom->val + c * LCOMMUNITY_SIZE, lval->val, LCOMMUNITY_SIZE);
111
112 return 1;
113 }
114
115 /* This function takes pointer to Large Communites strucutre then
116 create a new Large Communities structure by uniq and sort each
117 Exteneded Communities value. */
118 static struct lcommunity *
lcommunity_uniq_sort(struct bgp_peer * peer,struct lcommunity * lcom)119 lcommunity_uniq_sort (struct bgp_peer *peer, struct lcommunity *lcom)
120 {
121 int i;
122 struct lcommunity *new;
123 struct lcommunity_val *lval;
124
125 if (!lcom) return NULL;
126
127 new = lcommunity_new (peer);
128
129 for (i = 0; i < lcom->size; i++)
130 {
131 lval = (struct lcommunity_val *) (lcom->val + (i * LCOMMUNITY_SIZE));
132 lcommunity_add_val (peer, new, lval);
133 }
134 return new;
135 }
136
137 /* Parse Large Communites Attribute in BGP packet. */
138 struct lcommunity *
lcommunity_parse(struct bgp_peer * peer,u_int8_t * pnt,u_short length)139 lcommunity_parse (struct bgp_peer *peer, u_int8_t *pnt, u_short length)
140 {
141 struct lcommunity tmp;
142 struct lcommunity *new;
143
144 /* Length check. */
145 if (length % LCOMMUNITY_SIZE)
146 return NULL;
147
148 /* Prepare tmporary structure for making a new Large Communities
149 Attribute. */
150 tmp.size = length / LCOMMUNITY_SIZE;
151 tmp.val = pnt;
152
153 /* Create a new Large Communities Attribute by uniq and sort each
154 Large Communities value */
155 new = lcommunity_uniq_sort (peer, &tmp);
156
157 return lcommunity_intern (peer, new);
158 }
159
160 /* Intern Large Communities Attribute. */
161 struct lcommunity *
lcommunity_intern(struct bgp_peer * peer,struct lcommunity * lcom)162 lcommunity_intern (struct bgp_peer *peer, struct lcommunity *lcom)
163 {
164 struct bgp_rt_structs *inter_domain_routing_db;
165 struct lcommunity *find;
166
167 if (!peer) return NULL;
168
169 inter_domain_routing_db = bgp_select_routing_db(peer->type);
170
171 if (!inter_domain_routing_db) return NULL;
172
173 assert (lcom->refcnt == 0);
174
175 find = (struct lcommunity *) hash_get(peer, inter_domain_routing_db->lcomhash, lcom, hash_alloc_intern);
176
177 if (find != lcom)
178 lcommunity_free (lcom);
179
180 find->refcnt++;
181
182 if (! find->str)
183 find->str = lcommunity_lcom2str (peer, find);
184
185 return find;
186 }
187
188 /* Unintern Large Communities Attribute. */
189 void
lcommunity_unintern(struct bgp_peer * peer,struct lcommunity * lcom)190 lcommunity_unintern (struct bgp_peer *peer, struct lcommunity *lcom)
191 {
192 struct bgp_rt_structs *inter_domain_routing_db;
193 struct lcommunity *ret = NULL;
194 (void) ret;
195
196 if (!peer) return;
197
198 inter_domain_routing_db = bgp_select_routing_db(peer->type);
199
200 if (!inter_domain_routing_db) return;
201
202 if (lcom->refcnt)
203 lcom->refcnt--;
204
205 /* Pull off from hash. */
206 if (lcom->refcnt == 0) {
207 /* Large community must be in the hash. */
208 ret = (struct lcommunity *) hash_release(inter_domain_routing_db->lcomhash, lcom);
209 assert (ret != NULL);
210
211 lcommunity_free(lcom);
212 }
213 }
214
215 /* Utinity function to make hash key. */
216 unsigned int
lcommunity_hash_make(void * arg)217 lcommunity_hash_make (void *arg)
218 {
219 const struct lcommunity *lcom = arg;
220 int c;
221 unsigned int key;
222 u_int8_t *pnt;
223
224 key = 0;
225 pnt = lcom->val;
226
227 for (c = 0; c < lcom->size * LCOMMUNITY_SIZE; c++)
228 key += pnt[c];
229
230 return key;
231 }
232
233 /* Compare two Large Communities Attribute structure. */
234 int
lcommunity_cmp(const void * arg1,const void * arg2)235 lcommunity_cmp (const void *arg1, const void *arg2)
236 {
237 const struct lcommunity *lcom1 = arg1;
238 const struct lcommunity *lcom2 = arg2;
239
240 return (lcom1->size == lcom2->size
241 && memcmp (lcom1->val, lcom2->val, lcom1->size * LCOMMUNITY_SIZE) == 0);
242 }
243
244 /* Initialize Large Comminities related hash. */
245 void
lcommunity_init(int buckets,struct hash ** loc_lcomhash)246 lcommunity_init (int buckets, struct hash **loc_lcomhash)
247 {
248 (*loc_lcomhash) = hash_create (buckets, lcommunity_hash_make, lcommunity_cmp);
249 }
250
251 char *
lcommunity_lcom2str(struct bgp_peer * peer,struct lcommunity * lcom)252 lcommunity_lcom2str (struct bgp_peer *peer, struct lcommunity *lcom)
253 {
254 struct bgp_misc_structs *bms;
255 int idx, str_pnt, str_size, first = TRUE;
256 u_int32_t npart1, npart2, npart3;
257 u_int32_t hpart1, hpart2, hpart3;
258 char *str_buf = NULL;
259 u_int8_t *pnt;
260
261 if (!peer) return NULL;
262
263 bms = bgp_select_misc_db(peer->type);
264
265 if (!bms) return NULL;
266
267 if (lcom->size == 0) {
268 str_buf = malloc(1);
269 if (!str_buf) goto exit_lane;
270
271 str_buf[0] = '\0';
272
273 return str_buf;
274 }
275
276 for (idx = 0, str_pnt = 0, str_size = 0; idx < lcom->size; idx++) {
277 str_size += (LCOMMUNITY_STR_DEFAULT_LEN + 1);
278
279 if (!first) str_buf = realloc(str_buf, str_size);
280 else str_buf = malloc(str_size);
281
282 if (!str_buf) goto exit_lane;
283
284 if (!first) str_buf[str_pnt++] = ' ';
285 pnt = lcom->val + (idx * LCOMMUNITY_SIZE);
286 memcpy(&npart1, pnt, LCOMMUNITY_PART_SIZE);
287 memcpy(&npart2, (pnt + LCOMMUNITY_PART_SIZE), LCOMMUNITY_PART_SIZE);
288 memcpy(&npart3, (pnt + LCOMMUNITY_PART_SIZE + LCOMMUNITY_PART_SIZE), LCOMMUNITY_PART_SIZE);
289 hpart1 = ntohl(npart1);
290 hpart2 = ntohl(npart2);
291 hpart3 = ntohl(npart3);
292 sprintf(&str_buf[str_pnt], "%u:%u:%u", hpart1, hpart2, hpart3);
293 str_pnt = strlen(str_buf);
294
295 first = FALSE;
296 }
297
298 return str_buf;
299
300 exit_lane:
301 Log(LOG_ERR, "ERROR ( %s/%s ): malloc() failed (lcommunity_lcom2str). Exiting ..\n", config.name, bms->log_str);
302 exit_gracefully(1);
303
304 return NULL; /* silence compiler warning */
305 }
306
lcommunity_dup(struct lcommunity * lcom)307 struct lcommunity *lcommunity_dup(struct lcommunity *lcom)
308 {
309 struct lcommunity *new;
310
311 new = malloc(sizeof(struct lcommunity));
312
313 new->size = lcom->size;
314
315 if (new->size) {
316 new->val = malloc(lcom->size * LCOMMUNITY_SIZE);
317 memcpy (new->val, lcom->val, lcom->size * LCOMMUNITY_SIZE);
318 }
319 else new->val = NULL;
320
321 return new;
322 }
323