1 /* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
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:
17 * The above copyright notice and this permission notice shall be
18 * included in all copies or substantial portions of the Software.
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
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.
36 #include <arpa/inet.h>
39 #include "openflow/nicira-ext.h"
41 #include "openflow/openflow.h"
42 #include "port-watcher.h"
44 #define THIS_MODULE VLM_snat
47 struct snat_port_conf {
49 struct nx_snat_config config;
53 struct port_watcher *pw;
54 struct list port_list;
58 /* Source-NAT configuration monitor. */
59 #define SNAT_CMD_LEN 1024
61 /* Commands to configure iptables. There is no programmatic interface
62 * to iptables from the kernel, so we're stuck making command-line calls
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"
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"
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"
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"
78 snat_add_rules(const struct nx_snat_config *sc, const uint8_t *dev_name)
80 char command[SNAT_CMD_LEN];
81 char ip_str_start[16];
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));
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. */
95 /* Flush the chain that does the SNAT. */
96 snprintf(command, sizeof(command), SNAT_FLUSH_CHAIN_CMD, dev_name);
99 /* We always try to create the a new chain. */
100 snprintf(command, sizeof(command), SNAT_ADD_CHAIN_CMD, dev_name);
103 /* Disassociate any old SNAT chain from the POSTROUTING chain. */
104 snprintf(command, sizeof(command), SNAT_UNSET_CHAIN_CMD, dev_name,
108 /* Associate the new chain with the POSTROUTING hook. */
109 snprintf(command, sizeof(command), SNAT_CONF_CHAIN_CMD, dev_name,
111 if (system(command) != 0) {
112 VLOG_ERR("SNAT: problem flushing chain for add");
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");
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");
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");
149 snat_del_rules(const uint8_t *dev_name)
151 char command[SNAT_CMD_LEN];
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");
160 /* Disassociate the SNAT chain from the POSTROUTING chain. */
161 snprintf(command, sizeof(command), SNAT_UNSET_CHAIN_CMD, dev_name,
163 if (system(command) != 0) {
164 VLOG_ERR("SNAT: problem unsetting chain");
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");
177 snat_config(const struct nx_snat_config *sc, struct snat_data *snat)
179 struct snat_port_conf *c, *spc=NULL;
180 const uint8_t *netdev_name;
182 netdev_name = (const uint8_t *) port_watcher_get_name(snat->pw,
188 LIST_FOR_EACH(c, struct snat_port_conf, node, &snat->port_list) {
189 if (c->config.port == sc->port) {
195 if (sc->command == NXSC_ADD) {
197 spc = xmalloc(sizeof(*c));
199 VLOG_ERR("SNAT: no memory for new entry");
202 list_push_back(&snat->port_list, &spc->node);
204 memcpy(&spc->config, sc, sizeof(spc->config));
205 snat_add_rules(sc, netdev_name);
207 snat_del_rules(netdev_name);
208 list_remove(&spc->node);
213 snat_remote_packet_cb(struct relay *r, void *snat_)
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;
222 if (msg->size < sizeof(struct nx_act_config)) {
226 if (request->header.type != OFPT_VENDOR
227 || request->vendor != htonl(NX_VENDOR_ID)
228 || request->subtype != htonl(NXT_ACT_SET_CONFIG)) {
232 /* We're only interested in attempts to configure SNAT */
233 if (nac->type != htons(NXAST_SNAT)) {
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);
246 snat_port_changed_cb(uint16_t port_no,
247 const struct ofp_phy_port *old,
248 const struct ofp_phy_port *new,
251 struct snat_data *snat = snat_;
252 struct snat_port_conf *c;
254 /* We're only interested in ports that went away */
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);
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 */
273 NULL, /* closing_cb */
277 snat_start(struct secchan *secchan, struct port_watcher *pw)
280 struct snat_data *snat;
282 ret = system(SNAT_FLUSH_ALL_CMD);
284 VLOG_ERR("SNAT: problem flushing tables");
287 snat = xcalloc(1, sizeof *snat);
289 list_init(&snat->port_list);
291 port_watcher_register_callback(pw, snat_port_changed_cb, snat);
292 add_hook(secchan, &snat_hook_class, snat);