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