1 /*
2  * Copyright (C) 2016      SUSE Linux GmbH
3  * Author Juergen Gross <jgross@suse.com>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU Lesser General Public License as published
7  * by the Free Software Foundation; version 2.1 only. with the special
8  * exception on linking described in file LICENSE.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU Lesser General Public License for more details.
14  */
15 
16 #include "libxl_osdeps.h"
17 
18 #include "libxl_internal.h"
19 
libxl_mac_to_device_nic(libxl_ctx * ctx,uint32_t domid,const char * mac,libxl_device_nic * nic)20 int libxl_mac_to_device_nic(libxl_ctx *ctx, uint32_t domid,
21                             const char *mac, libxl_device_nic *nic)
22 {
23     GC_INIT(ctx);
24     libxl_device_nic *nics;
25     int nb, rc, i;
26     libxl_mac mac_n;
27 
28     rc = libxl__parse_mac(mac, mac_n);
29     if (rc)
30         return rc;
31 
32     nics = libxl__device_list(gc, &libxl__nic_devtype, domid, &nb);
33     if (!nics)
34         return ERROR_FAIL;
35 
36     memset(nic, 0, sizeof (libxl_device_nic));
37 
38     rc = ERROR_INVAL;
39     for (i = 0; i < nb; ++i) {
40         if (!libxl__compare_macs(&mac_n, &nics[i].mac)) {
41             *nic = nics[i];
42             rc = 0;
43             i++; /* Do not dispose this NIC on exit path */
44             break;
45         }
46         libxl_device_nic_dispose(&nics[i]);
47     }
48 
49     for (; i<nb; i++)
50         libxl_device_nic_dispose(&nics[i]);
51 
52     free(nics);
53     return rc;
54 }
55 
libxl__device_nic_setdefault(libxl__gc * gc,uint32_t domid,libxl_device_nic * nic,bool hotplug)56 static int libxl__device_nic_setdefault(libxl__gc *gc, uint32_t domid,
57                                         libxl_device_nic *nic, bool hotplug)
58 {
59     int rc;
60 
61     if (!nic->mtu)
62         nic->mtu = 1492;
63     if (!nic->model) {
64         nic->model = strdup("rtl8139");
65         if (!nic->model) return ERROR_NOMEM;
66     }
67     if (libxl__mac_is_default(&nic->mac)) {
68         const uint8_t *r;
69         libxl_uuid uuid;
70 
71         libxl_uuid_generate(&uuid);
72         r = libxl_uuid_bytearray(&uuid);
73 
74         nic->mac[0] = 0x00;
75         nic->mac[1] = 0x16;
76         nic->mac[2] = 0x3e;
77         nic->mac[3] = r[0] & 0x7f;
78         nic->mac[4] = r[1];
79         nic->mac[5] = r[2];
80     }
81     if (!nic->bridge) {
82         nic->bridge = strdup("xenbr0");
83         if (!nic->bridge) return ERROR_NOMEM;
84     }
85     if ( !nic->script && asprintf(&nic->script, "%s/vif-bridge",
86                                   libxl__xen_script_dir_path()) < 0 )
87         return ERROR_FAIL;
88 
89     rc = libxl__resolve_domid(gc, nic->backend_domname, &nic->backend_domid);
90     if (rc < 0) return rc;
91 
92     switch (libxl__domain_type(gc, domid)) {
93     case LIBXL_DOMAIN_TYPE_HVM:
94         if (!nic->nictype) {
95             if (hotplug)
96                 nic->nictype = LIBXL_NIC_TYPE_VIF;
97             else
98                 nic->nictype = LIBXL_NIC_TYPE_VIF_IOEMU;
99         }
100         break;
101     case LIBXL_DOMAIN_TYPE_PVH:
102     case LIBXL_DOMAIN_TYPE_PV:
103         if (nic->nictype == LIBXL_NIC_TYPE_VIF_IOEMU) {
104             LOGD(ERROR, domid,
105             "trying to create PV or PVH guest with an emulated interface");
106             return ERROR_INVAL;
107         }
108         nic->nictype = LIBXL_NIC_TYPE_VIF;
109         break;
110     case LIBXL_DOMAIN_TYPE_INVALID:
111         return ERROR_FAIL;
112     default:
113         abort();
114     }
115 
116     return rc;
117 }
118 
libxl__update_config_nic(libxl__gc * gc,libxl_device_nic * dst,const libxl_device_nic * src)119 static void libxl__update_config_nic(libxl__gc *gc, libxl_device_nic *dst,
120                                      const libxl_device_nic *src)
121 {
122     dst->devid = src->devid;
123     dst->nictype = src->nictype;
124     libxl_mac_copy(CTX, &dst->mac, &src->mac);
125 }
126 
libxl__set_xenstore_nic(libxl__gc * gc,uint32_t domid,libxl_device_nic * nic,flexarray_t * back,flexarray_t * front,flexarray_t * ro_front)127 static int libxl__set_xenstore_nic(libxl__gc *gc, uint32_t domid,
128                                    libxl_device_nic *nic,
129                                    flexarray_t *back, flexarray_t *front,
130                                    flexarray_t *ro_front)
131 {
132     flexarray_grow(back, 2);
133 
134     if (nic->script)
135         flexarray_append_pair(back, "script",
136                               libxl__abs_path(gc, nic->script,
137                                               libxl__xen_script_dir_path()));
138 
139     if (nic->ifname) {
140         flexarray_append(back, "vifname");
141         flexarray_append(back, nic->ifname);
142     }
143 
144     if (nic->coloft_forwarddev) {
145         flexarray_append(back, "forwarddev");
146         flexarray_append(back, nic->coloft_forwarddev);
147     }
148 
149 #define MAYBE_ADD_COLO_ARGS(arg) ({                                       \
150     if (nic->colo_##arg) {                                                \
151         flexarray_append(back, "colo_"#arg);                              \
152         flexarray_append(back, nic->colo_##arg);                          \
153     }                                                                     \
154 })
155 
156     MAYBE_ADD_COLO_ARGS(sock_mirror_id);
157     MAYBE_ADD_COLO_ARGS(sock_mirror_ip);
158     MAYBE_ADD_COLO_ARGS(sock_mirror_port);
159     MAYBE_ADD_COLO_ARGS(sock_compare_pri_in_id);
160     MAYBE_ADD_COLO_ARGS(sock_compare_pri_in_ip);
161     MAYBE_ADD_COLO_ARGS(sock_compare_pri_in_port);
162     MAYBE_ADD_COLO_ARGS(sock_compare_sec_in_id);
163     MAYBE_ADD_COLO_ARGS(sock_compare_sec_in_ip);
164     MAYBE_ADD_COLO_ARGS(sock_compare_sec_in_port);
165     MAYBE_ADD_COLO_ARGS(sock_compare_notify_id);
166     MAYBE_ADD_COLO_ARGS(sock_compare_notify_ip);
167     MAYBE_ADD_COLO_ARGS(sock_compare_notify_port);
168     MAYBE_ADD_COLO_ARGS(sock_redirector0_id);
169     MAYBE_ADD_COLO_ARGS(sock_redirector0_ip);
170     MAYBE_ADD_COLO_ARGS(sock_redirector0_port);
171     MAYBE_ADD_COLO_ARGS(sock_redirector1_id);
172     MAYBE_ADD_COLO_ARGS(sock_redirector1_ip);
173     MAYBE_ADD_COLO_ARGS(sock_redirector1_port);
174     MAYBE_ADD_COLO_ARGS(sock_redirector2_id);
175     MAYBE_ADD_COLO_ARGS(sock_redirector2_ip);
176     MAYBE_ADD_COLO_ARGS(sock_redirector2_port);
177     MAYBE_ADD_COLO_ARGS(filter_mirror_queue);
178     MAYBE_ADD_COLO_ARGS(filter_mirror_outdev);
179     MAYBE_ADD_COLO_ARGS(filter_redirector0_queue);
180     MAYBE_ADD_COLO_ARGS(filter_redirector0_indev);
181     MAYBE_ADD_COLO_ARGS(filter_redirector0_outdev);
182     MAYBE_ADD_COLO_ARGS(filter_redirector1_queue);
183     MAYBE_ADD_COLO_ARGS(filter_redirector1_indev);
184     MAYBE_ADD_COLO_ARGS(filter_redirector1_outdev);
185     MAYBE_ADD_COLO_ARGS(compare_pri_in);
186     MAYBE_ADD_COLO_ARGS(compare_sec_in);
187     MAYBE_ADD_COLO_ARGS(compare_out);
188     MAYBE_ADD_COLO_ARGS(compare_notify_dev);
189 
190     MAYBE_ADD_COLO_ARGS(sock_sec_redirector0_id);
191     MAYBE_ADD_COLO_ARGS(sock_sec_redirector0_ip);
192     MAYBE_ADD_COLO_ARGS(sock_sec_redirector0_port);
193     MAYBE_ADD_COLO_ARGS(sock_sec_redirector1_id);
194     MAYBE_ADD_COLO_ARGS(sock_sec_redirector1_ip);
195     MAYBE_ADD_COLO_ARGS(sock_sec_redirector1_port);
196     MAYBE_ADD_COLO_ARGS(filter_sec_redirector0_queue);
197     MAYBE_ADD_COLO_ARGS(filter_sec_redirector0_indev);
198     MAYBE_ADD_COLO_ARGS(filter_sec_redirector0_outdev);
199     MAYBE_ADD_COLO_ARGS(filter_sec_redirector1_queue);
200     MAYBE_ADD_COLO_ARGS(filter_sec_redirector1_indev);
201     MAYBE_ADD_COLO_ARGS(filter_sec_redirector1_outdev);
202     MAYBE_ADD_COLO_ARGS(filter_sec_rewriter0_queue);
203     MAYBE_ADD_COLO_ARGS(checkpoint_host);
204     MAYBE_ADD_COLO_ARGS(checkpoint_port);
205 
206 #undef MAYBE_ADD_COLO_ARGS
207 
208     flexarray_append(back, "mac");
209     flexarray_append(back,GCSPRINTF(LIBXL_MAC_FMT, LIBXL_MAC_BYTES(nic->mac)));
210     if (nic->ip) {
211         flexarray_append(back, "ip");
212         flexarray_append(back, libxl__strdup(gc, nic->ip));
213     }
214     if (nic->gatewaydev) {
215         flexarray_append(back, "gatewaydev");
216         flexarray_append(back, libxl__strdup(gc, nic->gatewaydev));
217     }
218 
219     if (nic->rate_interval_usecs > 0) {
220         flexarray_append(back, "rate");
221         flexarray_append(back, GCSPRINTF("%"PRIu64",%"PRIu32"",
222                             nic->rate_bytes_per_interval,
223                             nic->rate_interval_usecs));
224     }
225 
226     flexarray_append(back, "bridge");
227     flexarray_append(back, libxl__strdup(gc, nic->bridge));
228     flexarray_append(back, "handle");
229     flexarray_append(back, GCSPRINTF("%d", nic->devid));
230     flexarray_append(back, "type");
231     flexarray_append(back, libxl__strdup(gc,
232                                      libxl_nic_type_to_string(nic->nictype)));
233 
234     flexarray_append(front, "handle");
235     flexarray_append(front, GCSPRINTF("%d", nic->devid));
236     flexarray_append(front, "mac");
237     flexarray_append(front, GCSPRINTF(
238                                     LIBXL_MAC_FMT, LIBXL_MAC_BYTES(nic->mac)));
239 
240     return 0;
241 }
242 
libxl__device_nic_add(libxl__egc * egc,uint32_t domid,libxl_device_nic * nic,libxl__ao_device * aodev)243 static void libxl__device_nic_add(libxl__egc *egc, uint32_t domid,
244                                   libxl_device_nic *nic,
245                                   libxl__ao_device *aodev)
246 {
247     libxl__device_add_async(egc, domid, &libxl__nic_devtype, nic, aodev);
248 }
249 
libxl__nic_from_xenstore(libxl__gc * gc,const char * libxl_path,libxl_devid devid,libxl_device_nic * nic)250 static int libxl__nic_from_xenstore(libxl__gc *gc, const char *libxl_path,
251                                     libxl_devid devid, libxl_device_nic *nic)
252 {
253     const char *tmp;
254     int rc;
255 
256     libxl_device_nic_init(nic);
257 
258     rc = libxl__xs_read_checked(gc, XBT_NULL,
259                                 GCSPRINTF("%s/handle", libxl_path), &tmp);
260     if (rc) goto out;
261     if (tmp)
262         nic->devid = atoi(tmp);
263     else
264         nic->devid = 0;
265 
266     rc = libxl__xs_read_checked(gc, XBT_NULL,
267                                 GCSPRINTF("%s/backend", libxl_path), &tmp);
268     if (rc) goto out;
269 
270     if (!tmp) {
271         LOG(ERROR, "nic %s does not exist (no backend path)", libxl_path);
272         rc = ERROR_FAIL;
273         goto out;
274     }
275     rc = libxl__backendpath_parse_domid(gc, tmp, &nic->backend_domid);
276     if (rc) goto out;
277 
278     /* nic->mtu = */
279 
280     rc = libxl__xs_read_checked(gc, XBT_NULL,
281                                 GCSPRINTF("%s/mac", libxl_path), &tmp);
282     if (rc) goto out;
283     if (tmp) {
284         rc = libxl__parse_mac(tmp, nic->mac);
285         if (rc) goto out;
286     } else {
287         memset(nic->mac, 0, sizeof(nic->mac));
288     }
289 
290     rc = libxl__xs_read_checked(NOGC, XBT_NULL,
291                                 GCSPRINTF("%s/ip", libxl_path),
292                                 (const char **)(&nic->ip));
293     if (rc) goto out;
294     rc = libxl__xs_read_checked(NOGC, XBT_NULL,
295                                 GCSPRINTF("%s/bridge", libxl_path),
296                                 (const char **)(&nic->bridge));
297     if (rc) goto out;
298     rc = libxl__xs_read_checked(NOGC, XBT_NULL,
299                                 GCSPRINTF("%s/script", libxl_path),
300                                 (const char **)(&nic->script));
301     if (rc) goto out;
302     rc = libxl__xs_read_checked(NOGC, XBT_NULL,
303                                 GCSPRINTF("%s/forwarddev", libxl_path),
304                                 (const char **)(&nic->coloft_forwarddev));
305     if (rc) goto out;
306 
307 #define CHECK_COLO_ARGS(arg) ({                                           \
308     rc = libxl__xs_read_checked(NOGC, XBT_NULL,                           \
309                                 GCSPRINTF("%s/colo_"#arg, libxl_path),    \
310                                 (const char **)(&nic->colo_##arg));       \
311     if (rc) goto out;                                                     \
312 })
313 
314     CHECK_COLO_ARGS(sock_mirror_id);
315     CHECK_COLO_ARGS(sock_mirror_ip);
316     CHECK_COLO_ARGS(sock_mirror_port);
317     CHECK_COLO_ARGS(sock_compare_pri_in_id);
318     CHECK_COLO_ARGS(sock_compare_pri_in_ip);
319     CHECK_COLO_ARGS(sock_compare_pri_in_port);
320     CHECK_COLO_ARGS(sock_compare_sec_in_id);
321     CHECK_COLO_ARGS(sock_compare_sec_in_ip);
322     CHECK_COLO_ARGS(sock_compare_sec_in_port);
323     CHECK_COLO_ARGS(sock_compare_notify_id);
324     CHECK_COLO_ARGS(sock_compare_notify_ip);
325     CHECK_COLO_ARGS(sock_compare_notify_port);
326     CHECK_COLO_ARGS(sock_redirector0_id);
327     CHECK_COLO_ARGS(sock_redirector0_ip);
328     CHECK_COLO_ARGS(sock_redirector0_port);
329     CHECK_COLO_ARGS(sock_redirector1_id);
330     CHECK_COLO_ARGS(sock_redirector1_ip);
331     CHECK_COLO_ARGS(sock_redirector1_port);
332     CHECK_COLO_ARGS(sock_redirector2_id);
333     CHECK_COLO_ARGS(sock_redirector2_ip);
334     CHECK_COLO_ARGS(sock_redirector2_port);
335     CHECK_COLO_ARGS(filter_mirror_queue);
336     CHECK_COLO_ARGS(filter_mirror_outdev);
337     CHECK_COLO_ARGS(filter_redirector0_queue);
338     CHECK_COLO_ARGS(filter_redirector0_indev);
339     CHECK_COLO_ARGS(filter_redirector0_outdev);
340     CHECK_COLO_ARGS(filter_redirector1_queue);
341     CHECK_COLO_ARGS(filter_redirector1_indev);
342     CHECK_COLO_ARGS(filter_redirector1_outdev);
343     CHECK_COLO_ARGS(compare_pri_in);
344     CHECK_COLO_ARGS(compare_sec_in);
345     CHECK_COLO_ARGS(compare_out);
346     CHECK_COLO_ARGS(compare_notify_dev);
347     CHECK_COLO_ARGS(sock_sec_redirector0_id);
348     CHECK_COLO_ARGS(sock_sec_redirector0_ip);
349     CHECK_COLO_ARGS(sock_sec_redirector0_port);
350     CHECK_COLO_ARGS(sock_sec_redirector1_id);
351     CHECK_COLO_ARGS(sock_sec_redirector1_ip);
352     CHECK_COLO_ARGS(sock_sec_redirector1_port);
353     CHECK_COLO_ARGS(filter_sec_redirector0_queue);
354     CHECK_COLO_ARGS(filter_sec_redirector0_indev);
355     CHECK_COLO_ARGS(filter_sec_redirector0_outdev);
356     CHECK_COLO_ARGS(filter_sec_redirector1_queue);
357     CHECK_COLO_ARGS(filter_sec_redirector1_indev);
358     CHECK_COLO_ARGS(filter_sec_redirector1_outdev);
359     CHECK_COLO_ARGS(filter_sec_rewriter0_queue);
360     CHECK_COLO_ARGS(checkpoint_host);
361     CHECK_COLO_ARGS(checkpoint_port);
362 
363 #undef CHECK_COLO_ARGS
364 
365     /* vif_ioemu nics use the same xenstore entries as vif interfaces */
366     rc = libxl__xs_read_checked(gc, XBT_NULL,
367                                 GCSPRINTF("%s/type", libxl_path), &tmp);
368     if (rc) goto out;
369     if (tmp) {
370         rc = libxl_nic_type_from_string(tmp, &nic->nictype);
371         if (rc) goto out;
372     } else {
373         nic->nictype = LIBXL_NIC_TYPE_VIF;
374     }
375     nic->model = NULL; /* XXX Only for TYPE_IOEMU */
376     nic->ifname = NULL; /* XXX Only for TYPE_IOEMU */
377 
378     rc = 0;
379  out:
380     return rc;
381 }
382 
libxl_device_nic_list(libxl_ctx * ctx,uint32_t domid,int * num)383 libxl_device_nic *libxl_device_nic_list(libxl_ctx *ctx, uint32_t domid, int *num)
384 {
385     libxl_device_nic *r;
386 
387     GC_INIT(ctx);
388 
389     r = libxl__device_list(gc, &libxl__nic_devtype, domid, num);
390 
391     GC_FREE;
392 
393     return r;
394 }
395 
libxl_device_nic_list_free(libxl_device_nic * list,int num)396 void libxl_device_nic_list_free(libxl_device_nic* list, int num)
397 {
398     libxl__device_list_free(&libxl__nic_devtype, list, num);
399 }
400 
libxl_device_nic_getinfo(libxl_ctx * ctx,uint32_t domid,const libxl_device_nic * nic,libxl_nicinfo * nicinfo)401 int libxl_device_nic_getinfo(libxl_ctx *ctx, uint32_t domid,
402                               const libxl_device_nic *nic,
403                               libxl_nicinfo *nicinfo)
404 {
405     GC_INIT(ctx);
406     char *nicpath, *libxl_path;
407     char *val;
408     int rc;
409 
410     nicinfo->devid = nic->devid;
411 
412     nicpath = libxl__domain_device_frontend_path(gc, domid, nicinfo->devid,
413                                                  LIBXL__DEVICE_KIND_VIF);
414     libxl_path = libxl__domain_device_libxl_path(gc, domid, nicinfo->devid,
415                                                  LIBXL__DEVICE_KIND_VIF);
416     nicinfo->backend = xs_read(ctx->xsh, XBT_NULL,
417                                 GCSPRINTF("%s/backend", libxl_path), NULL);
418     if (!nicinfo->backend) {
419         GC_FREE;
420         return ERROR_FAIL;
421     }
422     rc = libxl__backendpath_parse_domid(gc, nicinfo->backend,
423                                         &nicinfo->backend_id);
424     if (rc) goto out;
425 
426     val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/state", nicpath));
427     nicinfo->state = val ? strtoul(val, NULL, 10) : -1;
428     val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/event-channel", nicpath));
429     nicinfo->evtch = val ? strtoul(val, NULL, 10) : -1;
430     val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/tx-ring-ref", nicpath));
431     nicinfo->rref_tx = val ? strtoul(val, NULL, 10) : -1;
432     val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/rx-ring-ref", nicpath));
433     nicinfo->rref_rx = val ? strtoul(val, NULL, 10) : -1;
434     nicinfo->frontend = libxl__strdup(NOGC, nicpath);
435     nicinfo->frontend_id = domid;
436 
437     rc = 0;
438  out:
439     GC_FREE;
440     return rc;
441 }
442 
libxl__device_nic_devname(libxl__gc * gc,uint32_t domid,uint32_t devid,libxl_nic_type type)443 const char *libxl__device_nic_devname(libxl__gc *gc,
444                                       uint32_t domid,
445                                       uint32_t devid,
446                                       libxl_nic_type type)
447 {
448     switch (type) {
449     case LIBXL_NIC_TYPE_VIF:
450         return GCSPRINTF(NETBACK_NIC_NAME, domid, devid);
451     case LIBXL_NIC_TYPE_VIF_IOEMU:
452         return GCSPRINTF(NETBACK_NIC_NAME TAP_DEVICE_SUFFIX, domid, devid);
453     default:
454         abort();
455     }
456 }
457 
libxl_device_nic_compare(const libxl_device_nic * d1,const libxl_device_nic * d2)458 static int libxl_device_nic_compare(const libxl_device_nic *d1,
459                                     const libxl_device_nic *d2)
460 {
461     return COMPARE_DEVID(d1, d2);
462 }
463 
libxl_device_nic_update_config(libxl__gc * gc,void * d,void * s)464 static void libxl_device_nic_update_config(libxl__gc *gc, void *d, void *s)
465 {
466     libxl__update_config_nic(gc, d, s);
467 }
468 
libxl__device_nic_set_devids(libxl__gc * gc,libxl_domain_config * d_config,uint32_t domid)469 int libxl__device_nic_set_devids(libxl__gc *gc, libxl_domain_config *d_config,
470                                  uint32_t domid)
471 {
472     int ret = 0;
473     int i;
474     size_t last_devid = -1;
475 
476     for (i = 0; i < d_config->num_nics; i++) {
477         /* We have to init the nic here, because we still haven't
478          * called libxl_device_nic_add when domcreate_launch_dm gets called,
479          * but qemu needs the nic information to be complete.
480          */
481         ret = libxl__device_nic_setdefault(gc, domid, &d_config->nics[i],
482                                            false);
483         if (ret) {
484             LOGD(ERROR, domid, "Unable to set nic defaults for nic %d", i);
485             goto out;
486         }
487 
488         if (d_config->nics[i].devid > last_devid)
489             last_devid = d_config->nics[i].devid;
490     }
491     for (i = 0; i < d_config->num_nics; i++) {
492         if (d_config->nics[i].devid < 0)
493             d_config->nics[i].devid = ++last_devid;
494     }
495 
496 out:
497     return ret;
498 }
499 
500 static LIBXL_DEFINE_UPDATE_DEVID(nic)
501 static LIBXL_DEFINE_DEVICE_FROM_TYPE(nic)
502 
503 LIBXL_DEFINE_DEVID_TO_DEVICE(nic)
504 LIBXL_DEFINE_DEVICE_ADD(nic)
505 LIBXL_DEFINE_DEVICES_ADD(nic)
506 LIBXL_DEFINE_DEVICE_REMOVE(nic)
507 
508 DEFINE_DEVICE_TYPE_STRUCT(nic, VIF,
509     .update_config = libxl_device_nic_update_config,
510     .from_xenstore = (device_from_xenstore_fn_t)libxl__nic_from_xenstore,
511     .set_xenstore_config = (device_set_xenstore_config_fn_t)
512                            libxl__set_xenstore_nic,
513 );
514 
515 /*
516  * Local variables:
517  * mode: C
518  * c-basic-offset: 4
519  * indent-tabs-mode: nil
520  * End:
521  */
522