Merge remote-tracking branch 'origin/ovs-dev' into bsd-port
[sliver-openvswitch.git] / lib / rtbsd.c
1 /*
2  * Copyright (c) 2011 Gaetano Catalli. 
3  * 
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  * 
7  *    1. Redistributions of source code must retain the above copyright notice,
8  *       this list of conditions and the following disclaimer.
9  * 
10  *    2. Redistributions in binary form must reproduce the above copyright
11  *       notice, this list of conditions and the following disclaimer in the
12  *       documentation and/or other materials provided with the distribution.
13  * 
14  * THIS SOFTWARE IS PROVIDED ``AS IS'' WITHOUT ANY WARRANTIES OF ANY KIND.
15  */
16
17 #include <config.h>
18
19 #include <unistd.h>
20 #include <errno.h>
21 #include <sys/socket.h>
22 #include <net/if.h>
23 #include <net/route.h>
24 #include <poll.h>
25
26 #include "coverage.h"
27 #include "socket-util.h"
28 #include "poll-loop.h"
29 #include "vlog.h"
30 #include "rtbsd.h"
31
32 VLOG_DEFINE_THIS_MODULE(rtbsd);
33
34 /* PF_ROUTE socket. */
35 static int notify_sock = -1;
36
37 /* All registered notifiers. */
38 static struct list all_notifiers = LIST_INITIALIZER(&all_notifiers);
39
40 static void rtbsd_report_change(const struct if_msghdr *);
41 static void rtbsd_report_notify_error(void);
42
43 /* Registers 'cb' to be called with auxiliary data 'aux' with network device
44  * change notifications.  The notifier is stored in 'notifier', which the
45  * caller must not modify or free.
46  *
47  * Returns 0 if successful, otherwise a positive errno value. */
48 int
49 rtbsd_notifier_register(struct rtbsd_notifier *notifier,
50                             rtbsd_notify_func *cb, void *aux)
51 {
52     if (notify_sock < 0) {
53         int error;
54         notify_sock = socket(PF_ROUTE, SOCK_RAW, 0);
55         if (notify_sock < 0) {
56             VLOG_WARN("could not create PF_ROUTE socket: %s",
57                       strerror(errno));
58             return errno;
59         }
60         error = set_nonblocking(notify_sock);
61         if (error) {
62             VLOG_WARN("error set_nonblocking PF_ROUTE socket: %s",
63                     strerror(error));
64             return error;
65         }
66     } else {
67         /* Catch up on notification work so that the new notifier won't
68          * receive any stale notifications. XXX*/
69         rtbsd_notifier_run();
70     }
71
72     list_push_back(&all_notifiers, &notifier->node);
73     notifier->cb = cb;
74     notifier->aux = aux;
75     return 0;
76 }
77
78 /* Cancels notification on 'notifier', which must have previously been
79  * registered with rtbsd_notifier_register(). */
80 void
81 rtbsd_notifier_unregister(struct rtbsd_notifier *notifier)
82 {
83     list_remove(&notifier->node);
84     if (list_is_empty(&all_notifiers)) {
85         close(notify_sock);
86         notify_sock = -1;
87     }
88 }
89
90 /* Calls all of the registered notifiers, passing along any as-yet-unreported
91  * netdev change events. */
92 void
93 rtbsd_notifier_run(void)
94 {
95     static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
96     struct if_msghdr msg;
97     if (notify_sock < 0) {
98         return;
99     }
100
101     for (;;) {
102         int retval;
103
104         msg.ifm_type = RTM_IFINFO;
105         msg.ifm_version = RTM_VERSION; //XXX check if necessary
106         
107         /* read from PF_ROUTE socket */
108         retval = read(notify_sock, (char *)&msg, sizeof(msg));
109         if (retval >= 0) {
110             /* received packet from PF_ROUTE socket 
111              * XXX check for bad packets */
112             if (msg.ifm_type == RTM_IFINFO) {
113                 rtbsd_report_change(&msg);
114             }
115         } else if (errno == EAGAIN) {
116             return;
117         } else {
118             if (errno == ENOBUFS) {
119                 VLOG_WARN_RL(&rl, "PF_ROUTE receive buffer overflowed");
120             } else {
121                 VLOG_WARN_RL(&rl, "error reading PF_ROUTE socket: %s",
122                              strerror(errno));
123             }
124             rtbsd_report_notify_error();
125         }
126     }
127 }
128
129 /* Causes poll_block() to wake up when network device change notifications are
130  * ready. */
131 void
132 rtbsd_notifier_wait(void)
133 {
134     if (notify_sock >= 0) {
135         poll_fd_wait(notify_sock, POLLIN);
136     }
137 }
138
139 static void
140 rtbsd_report_change(const struct if_msghdr *msg)
141 {
142     struct rtbsd_notifier *notifier;
143     struct rtbsd_change change;
144     
145     /*COVERAGE_INC(rtbsd_changed);*/  /* XXX update coverage-counters.c */
146
147     change.msg_type = msg->ifm_type; //XXX
148     change.if_index = msg->ifm_index;
149     if_indextoname(msg->ifm_index, change.if_name);
150     change.master_ifindex = 0; //XXX
151
152     LIST_FOR_EACH (notifier, node, &all_notifiers) {
153         notifier->cb(&change, notifier->aux);
154     }
155 }
156
157 /* If an error occurs the notifiers' callbacks are called with NULL changes */
158 static void
159 rtbsd_report_notify_error(void)
160 {
161     struct rtbsd_notifier *notifier;
162
163     LIST_FOR_EACH (notifier, node, &all_notifiers) {
164         notifier->cb(NULL, notifier->aux);
165     }
166 }