6778bd8cdf
Set of modifications and additions to establish a platform for implementing a hardware table in userspace. Modules in skeleton directory is used as a template for creating hardware dependent libraries.
268 lines
8.9 KiB
C
268 lines
8.9 KiB
C
/* Copyright (c) 2009 The Board of Trustees of The Leland Stanford
|
|
* Junior University
|
|
*
|
|
* We are making the OpenFlow specification and associated documentation
|
|
* (Software) available for public use and benefit with the expectation
|
|
* that others will use, modify and enhance the Software and contribute
|
|
* those enhancements back to the community. However, since we would
|
|
* like to make the Software available for broadest use, with as few
|
|
* restrictions as possible permission is hereby granted, free of
|
|
* charge, to any person obtaining a copy of this Software to deal in
|
|
* the Software under the copyrights without restriction, including
|
|
* without limitation the rights to use, copy, modify, merge, publish,
|
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
|
* permit persons to whom the Software is furnished to do so, subject to
|
|
* the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be
|
|
* included in all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
|
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*
|
|
* The name and trademarks of copyright holder(s) may NOT be used in
|
|
* advertising or publicity pertaining to the Software or any
|
|
* derivatives without specific, written prior permission.
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <arpa/inet.h>
|
|
#include "openflow/openflow-ext.h"
|
|
#include "of_ext_msg.h"
|
|
#include "netdev.h"
|
|
#include "datapath.h"
|
|
|
|
#define THIS_MODULE VLM_experimental
|
|
#include "vlog.h"
|
|
|
|
static int
|
|
new_queue(struct sw_port * port, struct sw_queue * queue,
|
|
uint32_t queue_id, uint16_t class_id,
|
|
struct ofp_queue_prop_min_rate * mr)
|
|
{
|
|
memset(queue, '\0', sizeof *queue);
|
|
queue->port = port;
|
|
queue->queue_id = queue_id;
|
|
/* class_id is the internal mapping to class. It is the offset
|
|
* in the array of queues for each port. Note that class_id is
|
|
* local to port, so we don't have any conflict.
|
|
* tc uses 16-bit class_id, so we cannot use the queue_id
|
|
* field */
|
|
queue->class_id = class_id;
|
|
queue->property = ntohs(mr->prop_header.property);
|
|
queue->min_rate = ntohs(mr->rate);
|
|
|
|
list_push_back(&port->queue_list, &queue->node);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
port_add_queue(struct sw_port *p, uint32_t queue_id,
|
|
struct ofp_queue_prop_min_rate * mr)
|
|
{
|
|
int queue_no;
|
|
for (queue_no = 1; queue_no < p->num_queues; queue_no++) {
|
|
struct sw_queue *q = &p->queues[queue_no];
|
|
if (!q->port) {
|
|
return new_queue(p,q,queue_id,queue_no,mr);
|
|
}
|
|
}
|
|
return EXFULL;
|
|
}
|
|
|
|
static int
|
|
port_delete_queue(struct sw_port *p UNUSED, struct sw_queue *q)
|
|
{
|
|
list_remove(&q->node);
|
|
memset(q,'\0', sizeof *q);
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
recv_of_exp_queue_delete(struct datapath *dp,
|
|
const struct sender *sender,
|
|
const void *oh)
|
|
{
|
|
struct sw_port *p;
|
|
struct sw_queue *q;
|
|
struct openflow_queue_command_header * ofq_delete;
|
|
struct ofp_packet_queue *opq;
|
|
|
|
uint16_t port_no;
|
|
uint32_t queue_id;
|
|
|
|
ofq_delete = (struct openflow_queue_command_header *)oh;
|
|
opq = (struct ofp_packet_queue *)ofq_delete->body;
|
|
port_no = ntohs(ofq_delete->port);
|
|
queue_id = ntohl(opq->queue_id);
|
|
|
|
p = dp_lookup_port(dp,port_no);
|
|
if (p->netdev) {
|
|
q = dp_lookup_queue(p,queue_id);
|
|
if (q) {
|
|
netdev_delete_class(p->netdev,q->class_id);
|
|
port_delete_queue(p,q);
|
|
}
|
|
else {
|
|
dp_send_error_msg(dp, sender, OFPET_QUEUE_OP_FAILED,
|
|
OFPQOFC_BAD_PORT, oh,
|
|
ntohs(ofq_delete->header.header.length));
|
|
}
|
|
}
|
|
else {
|
|
dp_send_error_msg(dp, sender, OFPET_QUEUE_OP_FAILED,
|
|
OFPQOFC_BAD_PORT, oh,
|
|
ntohs(ofq_delete->header.header.length));
|
|
}
|
|
}
|
|
|
|
/** Modifies/adds a queue. It first searches if a queue with
|
|
* id exists for this port. If yes it modifies it, otherwise adds
|
|
* a new configuration.
|
|
*
|
|
* @param dp the related datapath
|
|
* @param sender request source
|
|
* @param oh the openflow message for queue mod.
|
|
*/
|
|
static void
|
|
recv_of_exp_queue_modify(struct datapath *dp,
|
|
const struct sender *sender UNUSED,
|
|
const void *oh)
|
|
{
|
|
struct sw_port *p;
|
|
struct sw_queue *q;
|
|
struct openflow_queue_command_header * ofq_modify;
|
|
struct ofp_packet_queue *opq;
|
|
struct ofp_queue_prop_min_rate *mr;
|
|
|
|
int error = 0;
|
|
uint16_t port_no;
|
|
uint32_t queue_id;
|
|
|
|
|
|
ofq_modify = (struct openflow_queue_command_header *)oh;
|
|
opq = (struct ofp_packet_queue *)ofq_modify->body;
|
|
mr = (struct ofp_queue_prop_min_rate*)opq->properties;
|
|
|
|
/* Currently, we only accept queues with a single, min-rate property */
|
|
if ((ntohs(opq->len) != 24) ||
|
|
ntohs(mr->prop_header.property) != OFPQT_MIN_RATE) {
|
|
VLOG_ERR("Unknown queue configuration");
|
|
dp_send_error_msg(dp, sender, OFPET_QUEUE_OP_FAILED,
|
|
OFQ_ERR_DISCIPLINE, oh,
|
|
ntohs(ofq_modify->header.header.length));
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
port_no = ntohs(ofq_modify->port);
|
|
queue_id = ntohl(opq->queue_id);
|
|
|
|
p = dp_lookup_port(dp, port_no);
|
|
if (PORT_IN_USE(p)) {
|
|
q = dp_lookup_queue(p, queue_id);
|
|
if (q) {
|
|
/* queue exists - modify it */
|
|
error = netdev_change_class(p->netdev,q->class_id, ntohs(mr->rate));
|
|
if (error) {
|
|
VLOG_ERR("Failed to update queue %d", queue_id);
|
|
dp_send_error_msg(dp, sender, OFPET_QUEUE_OP_FAILED,
|
|
OFPQOFC_EPERM, oh,
|
|
ntohs(ofq_modify->header.header.length));
|
|
}
|
|
else {
|
|
q->property = ntohs(mr->prop_header.property);
|
|
q->min_rate = ntohs(mr->rate);
|
|
}
|
|
}
|
|
else {
|
|
/* create new queue */
|
|
error = port_add_queue(p,queue_id, mr);
|
|
if (error == EXFULL) {
|
|
dp_send_error_msg(dp, sender, OFPET_QUEUE_OP_FAILED,
|
|
OFPQOFC_EPERM, oh,
|
|
ntohs(ofq_modify->header.header.length));
|
|
return;
|
|
}
|
|
q = dp_lookup_queue(p, queue_id);
|
|
error = netdev_setup_class(p->netdev,q->class_id, ntohs(mr->rate));
|
|
if (error) {
|
|
VLOG_ERR("Failed to configure queue %d", queue_id);
|
|
dp_send_error_msg(dp, sender, OFPET_QUEUE_OP_FAILED,
|
|
OFPQOFC_BAD_QUEUE, oh,
|
|
ntohs(ofq_modify->header.header.length));
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
dp_send_error_msg(dp, sender, OFPET_QUEUE_OP_FAILED, OFPQOFC_BAD_PORT,
|
|
oh, ntohs(ofq_modify->header.header.length));
|
|
VLOG_ERR("Failed to create/modify queue - port %d doesn't exist",
|
|
port_no);
|
|
}
|
|
if (!error) {
|
|
if (IS_HW_PORT(p)) {
|
|
#if defined(OF_HW_PLAT) && !defined(USE_NETDEV)
|
|
error = dp->hw_drv->port_queue_config(dp->hw_drv, port_no,
|
|
queue_id, ntohs(mr->rate));
|
|
if (error < 0) {
|
|
VLOG_ERR("Failed to update HW port %d queue %d",
|
|
port_no, queue_id);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* Parses a set dp_desc message and uses it to set
|
|
* the dp_desc string in dp
|
|
*/
|
|
static void
|
|
recv_of_set_dp_desc(struct datapath *dp,
|
|
const struct sender *sender UNUSED,
|
|
const struct ofp_extension_header * exth)
|
|
{
|
|
struct openflow_ext_set_dp_desc * set_dp_desc = (struct openflow_ext_set_dp_desc * )
|
|
exth;
|
|
strncpy(dp->dp_desc, set_dp_desc->dp_desc, DESC_STR_LEN);
|
|
dp->dp_desc[DESC_STR_LEN-1] = 0; // force null for safety
|
|
}
|
|
|
|
/**
|
|
* Receives an experimental message and pass it
|
|
* to the appropriate handler
|
|
*/
|
|
int of_ext_recv_msg(struct datapath *dp, const struct sender *sender,
|
|
const void *oh)
|
|
{
|
|
const struct ofp_extension_header *ofexth = oh;
|
|
|
|
switch (ntohl(ofexth->subtype)) {
|
|
case OFP_EXT_QUEUE_MODIFY: {
|
|
recv_of_exp_queue_modify(dp,sender,oh);
|
|
return 0;
|
|
}
|
|
case OFP_EXT_QUEUE_DELETE: {
|
|
recv_of_exp_queue_delete(dp,sender,oh);
|
|
return 0;
|
|
}
|
|
case OFP_EXT_SET_DESC:
|
|
recv_of_set_dp_desc(dp,sender,ofexth);
|
|
return 0;
|
|
default:
|
|
VLOG_ERR("Received unknown command of type %d",
|
|
ntohl(ofexth->subtype));
|
|
return -EINVAL;
|
|
}
|
|
|
|
return -EINVAL;
|
|
}
|