/* $OpenBSD: ofpclient.c,v 1.7 2018/10/21 21:10:24 akoshibe Exp $ */ /* * Copyright (c) 2016 Reyk Floeter * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "switchd.h" #include "parser.h" void ofpclient_read(struct switch_connection *, int); int flowmod(struct switchd *, struct switch_connection *, struct parse_result *); int flowmod_test(struct switchd *, struct switch_connection *); void ofpclient(struct parse_result *res, struct passwd *pw) { struct switch_connection con; struct switchd sc; int s, timeout; struct sockaddr_un *un; memset(&sc, 0, sizeof(sc)); sc.sc_tap = -1; /* If no uri has been specified, try to connect to localhost */ if (res->uri.swa_addr.ss_family == AF_UNSPEC) { res->uri.swa_type = SWITCH_CONN_TCP; if (parsehostport("127.0.0.1", (struct sockaddr *)&res->uri.swa_addr, sizeof(res->uri.swa_addr)) != 0) fatal("could not parse address"); } memset(&con, 0, sizeof(con)); memcpy(&con.con_peer, &res->uri.swa_addr, sizeof(res->uri.swa_addr)); con.con_sc = ≻ /* * Connect and send the request */ switch (res->uri.swa_type) { case SWITCH_CONN_TCP: if ((s = socket(res->uri.swa_addr.ss_family, SOCK_STREAM, IPPROTO_TCP)) == -1) fatal("socket"); /* Use the default port if no port has been specified */ if (socket_getport(&con.con_peer) == 0) (void)socket_setport(&con.con_peer, SWITCHD_CTLR_PORT); if (connect(s, (struct sockaddr *)&con.con_peer, con.con_peer.ss_len) == -1) fatal("connect"); con.con_fd = s; break; case SWITCH_CONN_LOCAL: un = (struct sockaddr_un *)&res->uri.swa_addr; if (strncmp(un->sun_path, "/dev/switch", strlen("/dev/switch")) != 0) fatalx("device path not supported"); if ((s = open(un->sun_path, O_RDWR | O_NONBLOCK)) == -1) fatalx("failed to open %s", un->sun_path); con.con_fd = s; break; default: fatalx("connect type not supported"); } /* Drop privileges */ if (pledge("stdio", NULL) == -1) err(1, "pledge"); /* Set a default read timeout */ timeout = 3 * 1000; log_setverbose(res->verbose); ofp_send_hello(&sc, &con, OFP_V_1_3); ofpclient_read(&con, timeout); log_setverbose(res->quiet ? res->verbose : 2); switch (res->action) { case DUMP_DESC: ofp13_desc(&sc, &con); break; case DUMP_FEATURES: ofp_send_featuresrequest(&sc, &con); break; case DUMP_FLOWS: ofp13_flow_stats(&sc, &con, OFP_PORT_ANY, OFP_GROUP_ID_ANY, res->table); break; case DUMP_TABLES: ofp13_table_features(&sc, &con, res->table); break; case FLOW_ADD: case FLOW_DELETE: case FLOW_MODIFY: timeout = 0; flowmod(&sc, &con, res); break; default: fatalx("unsupported action"); } /* XXX */ ofpclient_read(&con, timeout); } int flowmod(struct switchd *sc, struct switch_connection *con, struct parse_result *res) { struct ofp_header *oh; struct ofp_flow_mod *fm; if (oflowmod_iclose(&res->fctx) == -1) goto err; if (oflowmod_close(&res->fctx) == -1) goto err; fm = res->fctx.ctx_fm; fm->fm_table_id = res->table; oh = &fm->fm_oh; if (ofp_validate(sc, &con->con_local, &con->con_peer, oh, res->fbuf, oh->oh_version) != 0) goto err; ofrelay_write(con, res->fbuf); return (0); err: (void)oflowmod_err(&res->fctx, __func__, __LINE__); log_warnx("invalid flow"); return (-1); } void ofpclient_read(struct switch_connection *con, int timeout) { uint8_t rbuf[0xffff]; ssize_t rlen; struct ofp_header *oh; struct ibuf *ibuf; struct pollfd pfd[1]; int nfds; /* Wait for response */ pfd[0].fd = con->con_fd; pfd[0].events = POLLIN; nfds = poll(pfd, 1, timeout); if (nfds == -1 || (pfd[0].revents & (POLLERR|POLLHUP|POLLNVAL))) fatal("poll error"); if (nfds == 0) { if (timeout) fatal("time out"); return; } if ((rlen = read(con->con_fd, rbuf, sizeof(rbuf))) == -1) fatal("read"); if (rlen == 0) fatal("connection closed"); if ((ibuf = ibuf_new(rbuf, rlen)) == NULL) fatal("ibuf_new"); if ((oh = ibuf_seek(ibuf, 0, sizeof(*oh))) == NULL) fatal("short header"); if (ofp_validate(con->con_sc, &con->con_peer, &con->con_local, oh, ibuf, oh->oh_version) != 0) fatal("ofp_validate"); if (con->con_state == OFP_STATE_CLOSED) { con->con_version = oh->oh_version; ofp_recv_hello(con->con_sc, con, oh, ibuf); con->con_state = OFP_STATE_ESTABLISHED; } ibuf_free(ibuf); } /* * stubs for ofp*.c */ void ofrelay_write(struct switch_connection *con, struct ibuf *buf) { struct msgbuf msgbuf; msgbuf_init(&msgbuf); msgbuf.fd = con->con_fd; ibuf_close(&msgbuf, buf); ibuf_write(&msgbuf); } struct switch_control * switch_add(struct switch_connection *con) { static struct switch_control sw; con->con_switch = &sw; return (&sw); } struct macaddr * switch_learn(struct switchd *sc, struct switch_control *sw, uint8_t *ea, uint32_t port) { return (NULL); } struct macaddr * switch_cached(struct switch_control *sw, uint8_t *ea) { return (NULL); } int ofp_nextstate(struct switchd *sc, struct switch_connection *con, enum ofp_state state) { int rv = 0; switch (con->con_state) { case OFP_STATE_CLOSED: if (state != OFP_STATE_HELLO_WAIT) return (-1); break; case OFP_STATE_HELLO_WAIT: if (state != OFP_STATE_FEATURE_WAIT) return (-1); rv = ofp_send_featuresrequest(sc, con); break; case OFP_STATE_FEATURE_WAIT: if (state != OFP_STATE_ESTABLISHED) return (-1); break; case OFP_STATE_ESTABLISHED: if (state != OFP_STATE_CLOSED) return (-1); break; default: return (-1); } /* Set the next state. */ con->con_state = state; return (rv); }