Never free an skb that has been passed to genlmsg_reply().
[sliver-openvswitch.git] / secchan / snat.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 "snat.h"
36 #include <arpa/inet.h>
37 #include <inttypes.h>
38 #include <stdlib.h>
39 #include "openflow/nicira-ext.h"
40 #include "ofpbuf.h"
41 #include "openflow/openflow.h"
42 #include "port-watcher.h"
43
44 #define THIS_MODULE VLM_snat
45 #include "vlog.h"
46
47 struct snat_port_conf {
48     struct list node;
49     struct nx_snat_config config;
50 };
51
52 struct snat_data {
53     struct port_watcher *pw;
54     struct list port_list;
55 };
56
57
58 /* Source-NAT configuration monitor. */
59 #define SNAT_CMD_LEN 1024
60
61 /* Commands to configure iptables.  There is no programmatic interface
62  * to iptables from the kernel, so we're stuck making command-line calls
63  * in user-space. */
64 #define SNAT_FLUSH_ALL_CMD "/sbin/iptables -t nat -F"
65 #define SNAT_FLUSH_CHAIN_CMD "/sbin/iptables -t nat -F of-snat-%s"
66
67 #define SNAT_ADD_CHAIN_CMD "/sbin/iptables -t nat -N of-snat-%s"
68 #define SNAT_CONF_CHAIN_CMD "/sbin/iptables -t nat -A POSTROUTING -o %s -j of-snat-%s"
69
70 #define SNAT_ADD_IP_CMD "/sbin/iptables -t nat -A of-snat-%s -j SNAT --to %s-%s"
71 #define SNAT_ADD_TCP_CMD "/sbin/iptables -t nat -A of-snat-%s -j SNAT -p TCP --to %s-%s:%d-%d"
72 #define SNAT_ADD_UDP_CMD "/sbin/iptables -t nat -A of-snat-%s -j SNAT -p UDP --to %s-%s:%d-%d"
73
74 #define SNAT_UNSET_CHAIN_CMD "/sbin/iptables -t nat -D POSTROUTING -o %s -j of-snat-%s"
75 #define SNAT_DEL_CHAIN_CMD "/sbin/iptables -t nat -X of-snat-%s"
76
77 static void 
78 snat_add_rules(const struct nx_snat_config *sc, const uint8_t *dev_name)
79 {
80     char command[SNAT_CMD_LEN];
81     char ip_str_start[16];
82     char ip_str_end[16];
83
84
85     snprintf(ip_str_start, sizeof ip_str_start, IP_FMT, 
86             IP_ARGS(&sc->ip_addr_start));
87     snprintf(ip_str_end, sizeof ip_str_end, IP_FMT, 
88             IP_ARGS(&sc->ip_addr_end));
89
90     /* We always attempt to remove existing entries, so that we know
91      * there's a pristine state for SNAT on the interface.  We just ignore 
92      * the results of these calls, since iptables will complain about 
93      * any non-existent entries. */
94
95     /* Flush the chain that does the SNAT. */
96     snprintf(command, sizeof(command), SNAT_FLUSH_CHAIN_CMD, dev_name);
97     system(command);
98
99     /* We always try to create the a new chain. */
100     snprintf(command, sizeof(command), SNAT_ADD_CHAIN_CMD, dev_name);
101     system(command);
102
103     /* Disassociate any old SNAT chain from the POSTROUTING chain. */
104     snprintf(command, sizeof(command), SNAT_UNSET_CHAIN_CMD, dev_name, 
105             dev_name);
106     system(command);
107
108     /* Associate the new chain with the POSTROUTING hook. */
109     snprintf(command, sizeof(command), SNAT_CONF_CHAIN_CMD, dev_name, 
110             dev_name);
111     if (system(command) != 0) {
112         VLOG_ERR("SNAT: problem flushing chain for add");
113         return;
114     }
115
116     /* If configured, restrict TCP source port ranges. */
117     if ((sc->tcp_start != 0) && (sc->tcp_end != 0)) {
118         snprintf(command, sizeof(command), SNAT_ADD_TCP_CMD, 
119                 dev_name, ip_str_start, ip_str_end,
120                 ntohs(sc->tcp_start), ntohs(sc->tcp_end));
121         if (system(command) != 0) {
122             VLOG_ERR("SNAT: problem adding TCP rule");
123             return;
124         }
125     }
126
127     /* If configured, restrict UDP source port ranges. */
128     if ((sc->udp_start != 0) && (sc->udp_end != 0)) {
129         snprintf(command, sizeof(command), SNAT_ADD_UDP_CMD, 
130                 dev_name, ip_str_start, ip_str_end,
131                 ntohs(sc->udp_start), ntohs(sc->udp_end));
132         if (system(command) != 0) {
133             VLOG_ERR("SNAT: problem adding UDP rule");
134             return;
135         }
136     }
137
138     /* Add a rule that covers all IP traffic that would not be covered
139      * by the prior TCP or UDP ranges. */
140     snprintf(command, sizeof(command), SNAT_ADD_IP_CMD, 
141             dev_name, ip_str_start, ip_str_end);
142     if (system(command) != 0) {
143         VLOG_ERR("SNAT: problem adding base rule");
144         return;
145     }
146 }
147
148 static void 
149 snat_del_rules(const uint8_t *dev_name)
150 {
151     char command[SNAT_CMD_LEN];
152
153     /* Flush the chain that does the SNAT. */
154     snprintf(command, sizeof(command), SNAT_FLUSH_CHAIN_CMD, dev_name);
155     if (system(command) != 0) {
156         VLOG_ERR("SNAT: problem flushing chain for deletion");
157         return;
158     }
159
160     /* Disassociate the SNAT chain from the POSTROUTING chain. */
161     snprintf(command, sizeof(command), SNAT_UNSET_CHAIN_CMD, dev_name, 
162             dev_name);
163     if (system(command) != 0) {
164         VLOG_ERR("SNAT: problem unsetting chain");
165         return;
166     }
167
168     /* Now we can finally delete our SNAT chain. */
169     snprintf(command, sizeof(command), SNAT_DEL_CHAIN_CMD, dev_name);
170     if (system(command) != 0) {
171         VLOG_ERR("SNAT: problem deleting chain");
172         return;
173     }
174 }
175
176 static void 
177 snat_config(const struct nx_snat_config *sc, struct snat_data *snat)
178 {
179     struct snat_port_conf *c, *spc=NULL;
180     const uint8_t *netdev_name;
181
182     netdev_name = (const uint8_t *) port_watcher_get_name(snat->pw,
183                                                           ntohs(sc->port));
184     if (!netdev_name) {
185         return;
186     }
187
188     LIST_FOR_EACH(c, struct snat_port_conf, node, &snat->port_list) {
189         if (c->config.port == sc->port) {
190             spc = c;
191             break;
192         }
193     }
194
195     if (sc->command == NXSC_ADD) {
196         if (!spc) {
197             spc = xmalloc(sizeof(*c));
198             if (!spc) {
199                 VLOG_ERR("SNAT: no memory for new entry");
200                 return;
201             }
202             list_push_back(&snat->port_list, &spc->node);
203         }
204         memcpy(&spc->config, sc, sizeof(spc->config));
205         snat_add_rules(sc, netdev_name);
206     } else if (spc) {
207         snat_del_rules(netdev_name);
208         list_remove(&spc->node);
209     }
210 }
211
212 static bool
213 snat_remote_packet_cb(struct relay *r, void *snat_)
214 {
215     struct snat_data *snat = snat_;
216     struct ofpbuf *msg = r->halves[HALF_REMOTE].rxbuf;
217     struct nicira_header *request = msg->data;
218     struct nx_act_config *nac = msg->data;
219     int n_configs, i;
220
221
222     if (msg->size < sizeof(struct nx_act_config)) {
223         return false;
224     }
225     request = msg->data;
226     if (request->header.type != OFPT_VENDOR
227         || request->vendor != htonl(NX_VENDOR_ID)
228         || request->subtype != htonl(NXT_ACT_SET_CONFIG)) {
229         return false;
230     }
231
232     /* We're only interested in attempts to configure SNAT */
233     if (nac->type != htons(NXAST_SNAT)) {
234         return false;
235     }
236
237     n_configs = (msg->size - sizeof *nac) / sizeof *nac->snat;
238     for (i=0; i<n_configs; i++) {
239         snat_config(&nac->snat[i], snat);
240     }
241
242     return false;
243 }
244
245 static void
246 snat_port_changed_cb(uint16_t port_no,
247                     const struct ofp_phy_port *old,
248                     const struct ofp_phy_port *new,
249                     void *snat_)
250 {
251     struct snat_data *snat = snat_;
252     struct snat_port_conf *c;
253
254     /* We're only interested in ports that went away */
255     if (old && !new) {
256         return;
257     }
258
259     LIST_FOR_EACH(c, struct snat_port_conf, node, &snat->port_list) {
260         if (c->config.port == old->port_no) {
261             snat_del_rules(old->name);
262             list_remove(&c->node);
263             return;
264         }
265     }
266 }
267
268 static struct hook_class snat_hook_class = {
269     NULL,                       /* local_packet_cb */
270     snat_remote_packet_cb,      /* remote_packet_cb */
271     NULL,                       /* periodic_cb */
272     NULL,                       /* wait_cb */
273     NULL,                       /* closing_cb */
274 };
275
276 void
277 snat_start(struct secchan *secchan, struct port_watcher *pw)
278 {
279     int ret;
280     struct snat_data *snat;
281
282     ret = system(SNAT_FLUSH_ALL_CMD); 
283     if (ret != 0) {
284         VLOG_ERR("SNAT: problem flushing tables");
285     }
286
287     snat = xcalloc(1, sizeof *snat);
288     snat->pw = pw;
289     list_init(&snat->port_list);
290
291     port_watcher_register_callback(pw, snat_port_changed_cb, snat);
292     add_hook(secchan, &snat_hook_class, snat);
293 }