vpp-swan: fix memory leaks
[vpp.git] / extras / strongswan / vpp_sswan / kernel_vpp_net.c
1 /*
2  * Copyright (c) 2022 Intel and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #include <utils/debug.h>
17 #include <vlibapi/api.h>
18 #include <vlibmemory/api.h>
19 #include <threading/thread.h>
20 #include <threading/mutex.h>
21
22 #define vl_typedefs
23 #define vl_endianfun
24 /* Include the (first) vlib-api API definition layer */
25 #include <vlibmemory/vl_memory_api_h.h>
26 /* Include the current layer (third) vpp API definition layer */
27 #include <vpp/api/vpe_types.api.h>
28 #include <vpp/api/vpe.api.h>
29
30 #include <vnet/ip-neighbor/ip_neighbor.api_enum.h>
31 #include <vnet/ip-neighbor/ip_neighbor.api_types.h>
32 #include <vnet/ip/ip.api_enum.h>
33 #include <vnet/ip/ip.api_types.h>
34 #include <vnet/interface.api_enum.h>
35 #include <vnet/interface.api_types.h>
36 #undef vl_typedefs
37 #undef vl_endianfun
38
39 #include "kernel_vpp_net.h"
40 #include "kernel_vpp_shared.h"
41
42 typedef struct private_kernel_vpp_net_t private_kernel_vpp_net_t;
43
44 /**
45  * Private data of kernel_vpp_net implementation.
46  */
47 struct private_kernel_vpp_net_t
48 {
49
50   /**
51    * Public interface.
52    */
53   kernel_vpp_net_t public;
54
55   /**
56    * Mutex to access interface list
57    */
58   mutex_t *mutex;
59
60   /**
61    * Known interfaces, as iface_t
62    */
63   linked_list_t *ifaces;
64
65   /**
66    * Inteface update thread
67    */
68   thread_t *net_update;
69
70   /**
71    * TRUE if interface events enabled
72    */
73   bool events_on;
74 };
75
76 /**
77  * Interface entry
78  */
79 typedef struct
80 {
81   /** interface index */
82   uint32_t index;
83   /** interface name */
84   char if_name[64];
85   /** list of known addresses, as host_t */
86   linked_list_t *addrs;
87   /** TRUE if up */
88   bool up;
89 } iface_t;
90
91 /**
92  * Address enumerator
93  */
94 typedef struct
95 {
96   /** implements enumerator_t */
97   enumerator_t public;
98   /** what kind of address should we enumerate? */
99   kernel_address_type_t which;
100   /** enumerator over interfaces */
101   enumerator_t *ifaces;
102   /** current enumerator over addresses, or NULL */
103   enumerator_t *addrs;
104   /** mutex to unlock on destruction */
105   mutex_t *mutex;
106 } addr_enumerator_t;
107
108 /**
109  * FIB path entry
110  */
111 typedef struct
112 {
113   chunk_t next_hop;
114   uint32_t sw_if_index;
115   uint8_t preference;
116 } fib_path_t;
117
118 /**
119  * Get an iface entry for a local address
120  */
121 static iface_t *
122 address2entry (private_kernel_vpp_net_t *this, host_t *ip)
123 {
124   enumerator_t *ifaces, *addrs;
125   iface_t *entry, *found = NULL;
126   host_t *host;
127
128   ifaces = this->ifaces->create_enumerator (this->ifaces);
129   while (!found && ifaces->enumerate (ifaces, &entry))
130     {
131       addrs = entry->addrs->create_enumerator (entry->addrs);
132       while (!found && addrs->enumerate (addrs, &host))
133         {
134           if (host->ip_equals (host, ip))
135             {
136               found = entry;
137             }
138         }
139       addrs->destroy (addrs);
140     }
141   ifaces->destroy (ifaces);
142
143   return found;
144 }
145
146 /**
147  * Add or remove a route
148  */
149 static status_t
150 manage_route (private_kernel_vpp_net_t *this, bool add, chunk_t dst,
151               uint8_t prefixlen, host_t *gtw, char *name)
152 {
153   char *out;
154   int out_len;
155   enumerator_t *enumerator;
156   iface_t *entry;
157   vl_api_ip_route_add_del_t *mp;
158   vl_api_ip_route_add_del_reply_t *rmp;
159   vl_api_fib_path_t *apath;
160   bool exists = FALSE;
161
162   this->mutex->lock (this->mutex);
163   enumerator = this->ifaces->create_enumerator (this->ifaces);
164   while (enumerator->enumerate (enumerator, &entry))
165     {
166       if (streq (name, entry->if_name))
167         {
168           exists = TRUE;
169           break;
170         }
171     }
172   enumerator->destroy (enumerator);
173   this->mutex->unlock (this->mutex);
174
175   if (!exists)
176     {
177       DBG1 (DBG_NET, "if_name %s not found", name);
178       return NOT_FOUND;
179     }
180
181   mp = vl_msg_api_alloc (sizeof (*mp) + sizeof (*apath));
182   memset (mp, 0, sizeof (*mp) + sizeof (*apath));
183   u16 msg_id = vl_msg_api_get_msg_index ((u8 *) "ip_route_add_del_b8ecfe0d");
184   mp->_vl_msg_id = ntohs (msg_id);
185   mp->is_add = add;
186   mp->route.prefix.len = prefixlen;
187   mp->route.n_paths = 1;
188   apath = &mp->route.paths[0];
189   apath->sw_if_index = ntohl (entry->index);
190   apath->rpf_id = ~0;
191   apath->weight = 1;
192   switch (dst.len)
193     {
194     case 4:
195       mp->route.prefix.address.af = ntohl (ADDRESS_IP4);
196       memcpy (&mp->route.prefix.address.un.ip4, dst.ptr, dst.len);
197       if (gtw)
198         {
199           chunk_t addr = gtw->get_address (gtw);
200           apath->proto = ntohl (FIB_API_PATH_NH_PROTO_IP4);
201           memcpy (&apath->nh.address.ip4, addr.ptr, dst.len);
202         }
203       break;
204     case 16:
205       mp->route.prefix.address.af = ntohl (ADDRESS_IP6);
206       memcpy (&mp->route.prefix.address.un.ip6, dst.ptr, dst.len);
207       if (gtw)
208         {
209           chunk_t addr = gtw->get_address (gtw);
210           apath->proto = ntohl (FIB_API_PATH_NH_PROTO_IP6);
211           memcpy (&apath->nh.address.ip6, addr.ptr, dst.len);
212         }
213       break;
214     default:
215       vl_msg_api_free (mp);
216       return FAILED;
217     }
218
219   if (vac->send (vac, (char *) mp, sizeof (*mp) + sizeof (*apath), &out,
220                  &out_len))
221     {
222       DBG1 (DBG_KNL, "vac %sing route failed", add ? "add" : "remov");
223       vl_msg_api_free (mp);
224       return FAILED;
225     }
226   rmp = (void *) out;
227   vl_msg_api_free (mp);
228   if (rmp->retval)
229     {
230       DBG1 (DBG_KNL, "%s route failed %d", add ? "add" : "delete",
231             ntohl (rmp->retval));
232       free (out);
233       return FAILED;
234     }
235   free (out);
236   return SUCCESS;
237 }
238
239 /**
240  * Check if an address or net (addr with prefix net bits) is in
241  * subnet (net with net_len net bits)
242  */
243 static bool
244 addr_in_subnet (chunk_t addr, int prefix, chunk_t net, int net_len)
245 {
246   static const u_char mask[] = {
247     0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe
248   };
249   int byte = 0;
250
251   if (net_len == 0)
252     { /* any address matches a /0 network */
253       return TRUE;
254     }
255   if (addr.len != net.len || net_len > 8 * net.len || prefix < net_len)
256     {
257       return FALSE;
258     }
259   /* scan through all bytes in network order */
260   while (net_len > 0)
261     {
262       if (net_len < 8)
263         {
264           return (mask[net_len] & addr.ptr[byte]) ==
265                  (mask[net_len] & net.ptr[byte]);
266         }
267       else
268         {
269           if (addr.ptr[byte] != net.ptr[byte])
270             {
271               return FALSE;
272             }
273           byte++;
274           net_len -= 8;
275         }
276     }
277   return TRUE;
278 }
279
280 /**
281  * Get a route: If "nexthop" the nexthop is returned, source addr otherwise
282  */
283 static host_t *
284 get_route (private_kernel_vpp_net_t *this, host_t *dest, int prefix,
285            bool nexthop, char **iface, host_t *src)
286 {
287   fib_path_t path;
288   char *out, *tmp;
289   int out_len, i, num;
290   vl_api_fib_path_t *fp;
291   host_t *addr = NULL;
292   enumerator_t *enumerator;
293   iface_t *entry;
294   int family;
295
296   path.sw_if_index = ~0;
297   path.preference = ~0;
298   path.next_hop = chunk_empty;
299
300   vl_api_ip_route_dump_t *mp;
301   vl_api_ip_route_details_t *rmp;
302
303   mp = vl_msg_api_alloc (sizeof (*mp));
304   clib_memset (mp, 0, sizeof (*mp));
305   u16 msg_id = vl_msg_api_get_msg_index ((u8 *) "ip_route_dump_b9d2e09e");
306   mp->_vl_msg_id = htons (msg_id);
307   mp->table.is_ip6 = dest->get_family (dest) == AF_INET6 ? 1 : 0;
308   if (vac->send_dump (vac, (char *) mp, sizeof (*mp), &out, &out_len))
309     {
310       vl_msg_api_free (mp);
311       DBG2 (DBG_KNL, "send VL_API_IP_ROUTE_ADD_DEL failed");
312       return NULL;
313     }
314   vl_msg_api_free (mp);
315
316   if (dest->get_family (dest) == AF_INET)
317     {
318       i = 0;
319       family = AF_INET;
320       if (prefix == -1)
321         prefix = 32;
322
323       tmp = out;
324       while (tmp < (out + out_len))
325         {
326           rmp = (void *) tmp;
327           num = rmp->route.n_paths;
328
329           if (rmp->route.prefix.len &&
330               addr_in_subnet (
331                 dest->get_address (dest), prefix,
332                 chunk_create (rmp->route.prefix.address.un.ip4, 4),
333                 rmp->route.prefix.len))
334             {
335               fp = rmp->route.paths;
336               for (i = 0; i < num; i++)
337                 {
338 #define IS_IP4_ANY(a) (a[0] == 0 && a[1] == 0 && a[2] == 0 & a[3] == 0)
339                   if (fp->type == FIB_API_PATH_TYPE_DROP)
340                     {
341                       fp++;
342                       continue;
343                     }
344                   if ((fp->preference < path.preference) ||
345                       (path.sw_if_index == ~0) ||
346                       IS_IP4_ANY (path.next_hop.ptr))
347                     {
348                       path.sw_if_index = ntohl (fp->sw_if_index);
349                       path.preference = fp->preference;
350                       if (path.next_hop.ptr)
351                         vl_msg_api_free (path.next_hop.ptr);
352                       path.next_hop = chunk_create (fp->nh.address.ip4, 4);
353                     }
354                   fp++;
355                 }
356             }
357           tmp += sizeof (*rmp) + (sizeof (*fp) * num);
358         }
359     }
360   else
361     {
362       DBG1 (DBG_KNL, "not yet support ip6");
363       return NULL;
364     }
365
366   if (path.next_hop.len)
367     {
368       if (nexthop)
369         {
370           if (iface)
371             {
372               *iface = NULL;
373               this->mutex->lock (this->mutex);
374               enumerator = this->ifaces->create_enumerator (this->ifaces);
375               while (enumerator->enumerate (enumerator, &entry))
376                 {
377                   if (entry->index == path.sw_if_index)
378                     {
379                       *iface = strdup (entry->if_name);
380                       break;
381                     }
382                 }
383               enumerator->destroy (enumerator);
384               this->mutex->unlock (this->mutex);
385             }
386           addr = host_create_from_chunk (family, path.next_hop, 0);
387         }
388       else
389         {
390           if (src)
391             {
392               addr = src->clone (src);
393             }
394         }
395     }
396
397   free (out);
398
399   return addr;
400 }
401
402 METHOD (enumerator_t, addr_enumerate, bool, addr_enumerator_t *this,
403         va_list args)
404 {
405   iface_t *entry;
406   host_t **host;
407
408   VA_ARGS_VGET (args, host);
409
410   while (TRUE)
411     {
412       while (!this->addrs)
413         {
414           if (!this->ifaces->enumerate (this->ifaces, &entry))
415             {
416               return FALSE;
417             }
418           if (!entry->up && !(this->which & ADDR_TYPE_DOWN))
419             {
420               continue;
421             }
422           this->addrs = entry->addrs->create_enumerator (entry->addrs);
423         }
424       if (this->addrs->enumerate (this->addrs, host))
425         {
426           return TRUE;
427         }
428       this->addrs->destroy (this->addrs);
429       this->addrs = NULL;
430     }
431 }
432
433 METHOD (enumerator_t, addr_destroy, void, addr_enumerator_t *this)
434 {
435   DESTROY_IF (this->addrs);
436   this->ifaces->destroy (this->ifaces);
437   this->mutex->unlock (this->mutex);
438   free (this);
439 }
440
441 METHOD (kernel_net_t, get_interface_name, bool, private_kernel_vpp_net_t *this,
442         host_t *ip, char **name)
443 {
444   iface_t *entry;
445
446   this->mutex->lock (this->mutex);
447   entry = address2entry (this, ip);
448   if (entry && name)
449     {
450       *name = strdup (entry->if_name);
451     }
452   this->mutex->unlock (this->mutex);
453
454   return entry != NULL;
455 }
456
457 METHOD (kernel_net_t, create_address_enumerator, enumerator_t *,
458         private_kernel_vpp_net_t *this, kernel_address_type_t which)
459 {
460   addr_enumerator_t *enumerator;
461
462   if (!(which & ADDR_TYPE_REGULAR))
463     {
464       /* we currently have no virtual, but regular IPs only */
465       return enumerator_create_empty ();
466     }
467
468   this->mutex->lock (this->mutex);
469
470   INIT(enumerator,
471         .public = {
472             .enumerate = enumerator_enumerate_default,
473             .venumerate = _addr_enumerate,
474             .destroy = _addr_destroy,
475         },
476         .which = which,
477         .ifaces = this->ifaces->create_enumerator(this->ifaces),
478         .mutex = this->mutex,
479     );
480   return &enumerator->public;
481 }
482
483 METHOD (kernel_net_t, get_source_addr, host_t *,
484         private_kernel_vpp_net_t *this, host_t *dest, host_t *src)
485 {
486   return get_route (this, dest, -1, FALSE, NULL, src);
487 }
488
489 METHOD (kernel_net_t, get_nexthop, host_t *, private_kernel_vpp_net_t *this,
490         host_t *dest, int prefix, host_t *src, char **iface)
491 {
492   return get_route (this, dest, prefix, TRUE, iface, src);
493 }
494
495 METHOD (kernel_net_t, add_ip, status_t, private_kernel_vpp_net_t *this,
496         host_t *virtual_ip, int prefix, char *iface_name)
497 {
498   return NOT_SUPPORTED;
499 }
500
501 METHOD (kernel_net_t, del_ip, status_t, private_kernel_vpp_net_t *this,
502         host_t *virtual_ip, int prefix, bool wait)
503 {
504   return NOT_SUPPORTED;
505 }
506
507 METHOD (kernel_net_t, add_route, status_t, private_kernel_vpp_net_t *this,
508         chunk_t dst_net, u_int8_t prefixlen, host_t *gateway, host_t *src_ip,
509         char *if_name)
510 {
511   return manage_route (this, TRUE, dst_net, prefixlen, gateway, if_name);
512 }
513
514 METHOD (kernel_net_t, del_route, status_t, private_kernel_vpp_net_t *this,
515         chunk_t dst_net, u_int8_t prefixlen, host_t *gateway, host_t *src_ip,
516         char *if_name)
517 {
518   return manage_route (this, FALSE, dst_net, prefixlen, gateway, if_name);
519 }
520
521 static void
522 iface_destroy (iface_t *this)
523 {
524   this->addrs->destroy_offset (this->addrs, offsetof (host_t, destroy));
525   free (this);
526 }
527
528 METHOD (kernel_net_t, destroy, void, private_kernel_vpp_net_t *this)
529 {
530   this->net_update->cancel (this->net_update);
531   this->mutex->destroy (this->mutex);
532   this->ifaces->destroy_function (this->ifaces, (void *) iface_destroy);
533   free (this);
534 }
535
536 /**
537  * Update addresses for an iface entry
538  */
539 static void
540 update_addrs (private_kernel_vpp_net_t *this, iface_t *entry)
541 {
542   char *out;
543   int out_len, i, num;
544   vl_api_ip_address_dump_t *mp;
545   vl_api_ip_address_details_t *rmp, *tmp;
546   linked_list_t *addrs;
547   host_t *host;
548   enumerator_t *enumerator;
549
550   mp = vl_msg_api_alloc (sizeof (*mp));
551   clib_memset (mp, 0, sizeof (*mp));
552   u16 msg_id = vl_msg_api_get_msg_index ((u8 *) "ip_address_dump_2d033de4");
553   mp->_vl_msg_id = htons (msg_id);
554   mp->sw_if_index = htonl (entry->index);
555   mp->is_ipv6 = 0;
556   if (vac->send_dump (vac, (char *) mp, sizeof (*mp), &out, &out_len))
557     {
558       DBG2 (DBG_NET, "update_addrs : send VL_API_IP_ADDRESS_DUMP ipv4 failed");
559       vl_msg_api_free (mp);
560       return;
561     }
562   num = out_len / sizeof (*rmp);
563   addrs = linked_list_create ();
564   tmp = (vl_api_ip_address_details_t *) out;
565   for (i = 0; i < num; i++)
566     {
567       rmp = tmp;
568       tmp += 1;
569       host = host_create_from_chunk (
570         AF_INET, chunk_create (rmp->prefix.address.un.ip4, 4), 0);
571       addrs->insert_last (addrs, host);
572     }
573   free (out);
574
575   mp->is_ipv6 = 1;
576   if (vac->send_dump (vac, (char *) mp, sizeof (*mp), &out, &out_len))
577     {
578       DBG2 (DBG_NET, "update_addrs : send VL_API_IP_ADDRESS_DUMP ipv6 failed");
579       vl_msg_api_free (mp);
580       return;
581     }
582   num = out_len / sizeof (*rmp);
583   tmp = (vl_api_ip_address_details_t *) out;
584   for (i = 0; i < num; i++)
585     {
586       rmp = tmp;
587       tmp += 1;
588       host = host_create_from_chunk (
589         AF_INET6, chunk_create (rmp->prefix.address.un.ip6, 16), 0);
590       addrs->insert_last (addrs, host);
591     }
592   vl_msg_api_free (mp);
593   free (out);
594
595   /* clean-up */
596   enumerator = entry->addrs->create_enumerator (entry->addrs);
597   while (enumerator->enumerate (enumerator, &host))
598     {
599       host->destroy (host);
600     }
601   enumerator->destroy (enumerator);
602   entry->addrs->destroy (entry->addrs);
603   entry->addrs =
604     linked_list_create_from_enumerator (addrs->create_enumerator (addrs));
605   addrs->destroy (addrs);
606 }
607
608 /**
609  * VPP API interface event callback
610  */
611 static void
612 event_cb (char *data, int data_len, void *ctx)
613 {
614   private_kernel_vpp_net_t *this = ctx;
615   vl_api_sw_interface_event_t *event;
616   iface_t *entry;
617   enumerator_t *enumerator;
618
619   event = (void *) data;
620   this->mutex->lock (this->mutex);
621   enumerator = this->ifaces->create_enumerator (this->ifaces);
622   while (enumerator->enumerate (enumerator, &entry))
623     {
624       if (entry->index == ntohl (event->sw_if_index))
625         {
626           if (event->deleted)
627             {
628               this->ifaces->remove_at (this->ifaces, enumerator);
629               DBG2 (DBG_NET, "interface deleted %u %s", entry->index,
630                     entry->if_name);
631               iface_destroy (entry);
632             }
633           else if (entry->up != (event->flags & IF_STATUS_API_FLAG_LINK_UP))
634             {
635               entry->up =
636                 (event->flags & IF_STATUS_API_FLAG_LINK_UP) ? TRUE : FALSE;
637               DBG2 (DBG_NET, "interface state changed %u %s %s", entry->index,
638                     entry->if_name, entry->up ? "UP" : "DOWN");
639             }
640           break;
641         }
642     }
643   enumerator->destroy (enumerator);
644   this->mutex->unlock (this->mutex);
645   free (data);
646 }
647
648 /**
649  * Inteface update thread (update interface list and interface address)
650  */
651 static void *
652 net_update_thread_fn (private_kernel_vpp_net_t *this)
653 {
654   status_t rv;
655   while (1)
656     {
657       char *out;
658       int out_len;
659       vl_api_sw_interface_dump_t *mp;
660       vl_api_sw_interface_details_t *rmp;
661       enumerator_t *enumerator;
662       iface_t *entry;
663
664       mp = vl_msg_api_alloc (sizeof (*mp));
665       memset (mp, 0, sizeof (*mp));
666       u16 msg_id =
667         vl_msg_api_get_msg_index ((u8 *) "sw_interface_dump_aa610c27");
668       mp->_vl_msg_id = htons (msg_id);
669       mp->name_filter_valid = 0;
670       rv = vac->send_dump (vac, (u8 *) mp, sizeof (*mp), &out, &out_len);
671       if (!rv)
672         {
673           int i, num;
674           this->mutex->lock (this->mutex);
675           enumerator = this->ifaces->create_enumerator (this->ifaces);
676           num = out_len / sizeof (*rmp);
677           rmp = (vl_api_sw_interface_details_t *) out;
678           for (i = 0; i < num; i++)
679             {
680               bool exists = FALSE;
681               if (i)
682                 rmp += 1;
683               while (enumerator->enumerate (enumerator, &entry))
684                 {
685                   if (entry->index == ntohl (rmp->sw_if_index))
686                     {
687                       exists = TRUE;
688                       break;
689                     }
690                 }
691               if (!exists)
692                 {
693                   INIT (entry, .index = ntohl (rmp->sw_if_index),
694                         .up = (rmp->flags & IF_STATUS_API_FLAG_LINK_UP) ?
695                                       TRUE :
696                                       FALSE,
697                         .addrs = linked_list_create (), );
698                   memcpy (entry->if_name, rmp->interface_name, 63);
699                   this->ifaces->insert_last (this->ifaces, entry);
700                 }
701               update_addrs (this, entry);
702             }
703           enumerator->destroy (enumerator);
704           this->mutex->unlock (this->mutex);
705           free (out);
706         }
707       vl_msg_api_free (mp);
708
709       if (!this->events_on)
710         {
711           vl_api_want_interface_events_t *emp;
712           api_main_t *am = vlibapi_get_main ();
713
714           emp = vl_msg_api_alloc (sizeof (*emp));
715           clib_memset (emp, 0, sizeof (*emp));
716           u16 msg_id =
717             vl_msg_api_get_msg_index ((u8 *) "want_interface_events_476f5a08");
718           emp->_vl_msg_id = ntohs (msg_id);
719           emp->enable_disable = 1;
720           emp->pid = ntohl (am->our_pid);
721           rv = vac->register_event (vac, (char *) emp, sizeof (*emp), event_cb,
722                                     VL_API_SW_INTERFACE_EVENT, this);
723           if (!rv)
724             this->events_on = TRUE;
725         }
726
727       sleep (2);
728     }
729   return NULL;
730 }
731
732 kernel_vpp_net_t *
733 kernel_vpp_net_create ()
734 {
735   private_kernel_vpp_net_t *this;
736
737   INIT(this,
738         .public = {
739             .interface = {
740                 .get_interface = _get_interface_name,
741                 .create_address_enumerator = _create_address_enumerator,
742                 .get_source_addr = _get_source_addr,
743                 .get_nexthop = _get_nexthop,
744                 .add_ip = _add_ip,
745                 .del_ip = _del_ip,
746                 .add_route = _add_route,
747                 .del_route = _del_route,
748                 .destroy = _destroy,
749             },
750         },
751         .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
752         .ifaces = linked_list_create(),
753         .events_on = FALSE,
754     );
755
756   this->net_update =
757     thread_create ((thread_main_t) net_update_thread_fn, this);
758
759   return &this->public;
760 }