For SNAT, don't store the pre-fragment L2 header before actions are applied.
[sliver-openvswitch.git] / secchan / status.c
1 /* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
2  * Junior University
3  *
4  * We are making the OpenFlow specification and associated documentation
5  * (Software) available for public use and benefit with the expectation
6  * that others will use, modify and enhance the Software and contribute
7  * those enhancements back to the community. However, since we would
8  * like to make the Software available for broadest use, with as few
9  * restrictions as possible permission is hereby granted, free of
10  * charge, to any person obtaining a copy of this Software to deal in
11  * the Software under the copyrights without restriction, including
12  * without limitation the rights to use, copy, modify, merge, publish,
13  * distribute, sublicense, and/or sell copies of the Software, and to
14  * permit persons to whom the Software is furnished to do so, subject to
15  * the following conditions:
16  *
17  * The above copyright notice and this permission notice shall be
18  * included in all copies or substantial portions of the Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23  * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
24  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
25  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
26  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27  * SOFTWARE.
28  *
29  * The name and trademarks of copyright holder(s) may NOT be used in
30  * advertising or publicity pertaining to the Software or any
31  * derivatives without specific, written prior permission.
32  */
33
34 #include <config.h>
35 #include "status.h"
36 #include <arpa/inet.h>
37 #include <assert.h>
38 #include <errno.h>
39 #include <unistd.h>
40 #include "dynamic-string.h"
41 #include "openflow/nicira-ext.h"
42 #include "ofpbuf.h"
43 #include "openflow/openflow.h"
44 #include "rconn.h"
45 #include "timeval.h"
46 #include "vconn.h"
47
48 #define THIS_MODULE VLM_status
49 #include "vlog.h"
50
51 struct switch_status_category {
52     char *name;
53     void (*cb)(struct status_reply *, void *aux);
54     void *aux;
55 };
56
57 struct switch_status {
58     const struct settings *s;
59     time_t booted;
60     struct switch_status_category *categories;
61     int n_categories, allocated_categories;
62 };
63
64 struct status_reply {
65     struct switch_status_category *category;
66     struct ds request;
67     struct ds output;
68 };
69
70 static bool
71 switch_status_remote_packet_cb(struct relay *r, void *ss_)
72 {
73     struct switch_status *ss = ss_;
74     struct rconn *rc = r->halves[HALF_REMOTE].rconn;
75     struct ofpbuf *msg = r->halves[HALF_REMOTE].rxbuf;
76     struct switch_status_category *c;
77     struct nicira_header *request;
78     struct nicira_header *reply;
79     struct status_reply sr;
80     struct ofpbuf *b;
81     int retval;
82
83     if (msg->size < sizeof(struct nicira_header)) {
84         return false;
85     }
86     request = msg->data;
87     if (request->header.type != OFPT_VENDOR
88         || request->vendor != htonl(NX_VENDOR_ID)
89         || request->subtype != htonl(NXT_STATUS_REQUEST)) {
90         return false;
91     }
92
93     sr.request.string = (void *) (request + 1);
94     sr.request.length = msg->size - sizeof *request;
95     ds_init(&sr.output);
96     for (c = ss->categories; c < &ss->categories[ss->n_categories]; c++) {
97         if (!memcmp(c->name, sr.request.string,
98                     MIN(strlen(c->name), sr.request.length))) {
99             sr.category = c;
100             c->cb(&sr, c->aux);
101         }
102     }
103     reply = make_openflow_xid(sizeof *reply + sr.output.length,
104                               OFPT_VENDOR, request->header.xid, &b);
105     reply->vendor = htonl(NX_VENDOR_ID);
106     reply->subtype = htonl(NXT_STATUS_REPLY);
107     memcpy(reply + 1, sr.output.string, sr.output.length);
108     retval = rconn_send(rc, b, NULL);
109     if (retval && retval != EAGAIN) {
110         VLOG_WARN("send failed (%s)", strerror(retval));
111     }
112     ds_destroy(&sr.output);
113     return true;
114 }
115
116 void
117 rconn_status_cb(struct status_reply *sr, void *rconn_)
118 {
119     struct rconn *rconn = rconn_;
120     time_t now = time_now();
121
122     status_reply_put(sr, "name=%s", rconn_get_name(rconn));
123     status_reply_put(sr, "state=%s", rconn_get_state(rconn));
124     status_reply_put(sr, "backoff=%d", rconn_get_backoff(rconn));
125     status_reply_put(sr, "is-connected=%s",
126                      rconn_is_connected(rconn) ? "true" : "false");
127     status_reply_put(sr, "sent-msgs=%u", rconn_packets_sent(rconn));
128     status_reply_put(sr, "received-msgs=%u", rconn_packets_received(rconn));
129     status_reply_put(sr, "attempted-connections=%u",
130                      rconn_get_attempted_connections(rconn));
131     status_reply_put(sr, "successful-connections=%u",
132                      rconn_get_successful_connections(rconn));
133     status_reply_put(sr, "last-connection=%ld",
134                      (long int) (now - rconn_get_last_connection(rconn)));
135     status_reply_put(sr, "time-connected=%lu",
136                      rconn_get_total_time_connected(rconn));
137     status_reply_put(sr, "state-elapsed=%u", rconn_get_state_elapsed(rconn));
138 }
139
140 static void
141 config_status_cb(struct status_reply *sr, void *s_)
142 {
143     const struct settings *s = s_;
144     size_t i;
145
146     for (i = 0; i < s->n_listeners; i++) {
147         status_reply_put(sr, "management%zu=%s", i, s->listener_names[i]);
148     }
149     if (s->probe_interval) {
150         status_reply_put(sr, "probe-interval=%d", s->probe_interval);
151     }
152     if (s->max_backoff) {
153         status_reply_put(sr, "max-backoff=%d", s->max_backoff);
154     }
155 }
156
157 static void
158 switch_status_cb(struct status_reply *sr, void *ss_)
159 {
160     struct switch_status *ss = ss_;
161     time_t now = time_now();
162
163     status_reply_put(sr, "now=%ld", (long int) now);
164     status_reply_put(sr, "uptime=%ld", (long int) (now - ss->booted));
165     status_reply_put(sr, "pid=%ld", (long int) getpid());
166 }
167
168 static struct hook_class switch_status_hook_class = {
169     NULL,                           /* local_packet_cb */
170     switch_status_remote_packet_cb, /* remote_packet_cb */
171     NULL,                           /* periodic_cb */
172     NULL,                           /* wait_cb */
173     NULL,                           /* closing_cb */
174 };
175
176 void
177 switch_status_start(struct secchan *secchan, const struct settings *s,
178                     struct switch_status **ssp)
179 {
180     struct switch_status *ss = xcalloc(1, sizeof *ss);
181     ss->s = s;
182     ss->booted = time_now();
183     switch_status_register_category(ss, "config",
184                                     config_status_cb, (void *) s);
185     switch_status_register_category(ss, "switch", switch_status_cb, ss);
186     *ssp = ss;
187     add_hook(secchan, &switch_status_hook_class, ss);
188 }
189
190 void
191 switch_status_register_category(struct switch_status *ss,
192                                 const char *category,
193                                 void (*cb)(struct status_reply *, void *aux),
194                                 void *aux)
195 {
196     struct switch_status_category *c;
197     if (ss->n_categories >= ss->allocated_categories) {
198         ss->allocated_categories = 1 + ss->allocated_categories * 2;
199         ss->categories = xrealloc(ss->categories,
200                                   (sizeof *ss->categories
201                                    * ss->allocated_categories));
202     }
203     c = &ss->categories[ss->n_categories++];
204     c->cb = cb;
205     c->aux = aux;
206     c->name = xstrdup(category);
207 }
208
209 void
210 status_reply_put(struct status_reply *sr, const char *content, ...)
211 {
212     size_t old_length = sr->output.length;
213     size_t added;
214     va_list args;
215
216     /* Append the status reply to the output. */
217     ds_put_format(&sr->output, "%s.", sr->category->name);
218     va_start(args, content);
219     ds_put_format_valist(&sr->output, content, args);
220     va_end(args);
221     if (ds_last(&sr->output) != '\n') {
222         ds_put_char(&sr->output, '\n');
223     }
224
225     /* Drop what we just added if it doesn't match the request. */
226     added = sr->output.length - old_length;
227     if (added < sr->request.length
228         || memcmp(&sr->output.string[old_length],
229                   sr->request.string, sr->request.length)) {
230         ds_truncate(&sr->output, old_length);
231     }
232 }