static void c_service_reconn_timer(evutil_socket_t fd UNUSED, short event UNUSED, void *arg) { mul_service_t *service = arg; struct timeval tv = { 5, 0 }; if (!service->conn.dead) return; c_log_debug("Retry Conn to service %s", service->service_name); if(!c_service_client_sock_init(service, service->server?:__server)) { c_log_debug("Connection to service %s restored", service->service_name); event_del((struct event *)(service->reconn_timer_event)); event_free((struct event *)(service->reconn_timer_event)); service->reconn_timer_event = NULL; service->conn.dead = 0; service->ext_ka_flag = 0; if (service->conn_update) { service->conn_update(service, MUL_SERVICE_UP); } mb(); service->valid_timer_event = evtimer_new(service->ev_base, c_service_validity_timer, (void *)service); evtimer_add(service->valid_timer_event, &tv); return; } evtimer_add(service->reconn_timer_event, &tv); }
/** * mul_print_route - * * Dump a route for printing */ static void mul_print_route(GSList *route_path) { #define TR_ROUTE_PBUF_SZ 4096 char *pbuf = calloc(1, TR_ROUTE_PBUF_SZ); int len = 0; GSList *iterator = NULL; rt_path_elem_t *rt_elem = NULL; len += snprintf(pbuf+len, TR_ROUTE_PBUF_SZ-len-1, "iROUTE: "); assert(len < TR_ROUTE_PBUF_SZ-1); for (iterator = route_path; iterator; iterator = iterator->next) { rt_elem = iterator->data; len += snprintf(pbuf+len, TR_ROUTE_PBUF_SZ-len-1, "Node(%d):Link(%hu)->", rt_elem->sw_alias, rt_elem->link.la); assert(len < TR_ROUTE_PBUF_SZ-1); } len += snprintf(pbuf+len, TR_ROUTE_PBUF_SZ-len-1, "||\r\n"); assert(len < TR_ROUTE_PBUF_SZ-1); c_log_debug("%s", pbuf); free(pbuf); }
static void c_service_read(evutil_socket_t fd, short events UNUSED, void *arg) { mul_service_t *hdl = arg; int ret; ret = c_socket_read_nonblock_loop(fd, hdl, &hdl->conn, C_SERV_RCV_BUF_SZ, (conn_proc_t)(hdl->ev_cb), of_get_data_len, of_hdr_valid, sizeof(struct ofp_header)); if (c_recvd_sock_dead(ret)) { hdl->conn.dead = 1; event_del((hdl->conn.rd_event)); event_del(hdl->conn.wr_event); event_free(hdl->conn.rd_event); event_free(hdl->conn.wr_event); hdl->conn.rd_event = NULL; hdl->conn.wr_event = NULL; close(hdl->conn.fd); c_log_debug("Service(%s) connection Broken..\n", hdl->service_name); free(hdl); perror("c_service_read"); } return; }
/** * hello_sw_add - * Switch join event notifier * * @sw : Switch arg passed by infra layer * @return : void */ static void hello_sw_add(mul_switch_t *sw) { /* Add few default flows in this switch */ hello_install_dfl_flows(sw->dpid); c_log_debug("switch dpid 0x%llx joined network", (unsigned long long)(sw->dpid)); }
// Note: // As main thread will be responsible for the handling // Tying up the main thread to the worker context in the main thread // is the key. The logic for the controller operates on ? // So, once the information has been put into the ?? , then the logic // can get the information from the c_switch_t->cmn_ctx where the // hashtable stores the DPID and switch information static int c_thread_event_loop_lib_support(struct c_main_ctx *main_ctx) { c_switch_t *sw = NULL; struct cbuf *b = NULL; c_log_debug("%s: tid(%u)", __FUNCTION__, (unsigned int)pthread_self()); // instead of looping on the socket fd, the main thread // will be looping on the cbuf. // Not considering the application threads for now //return event_base_dispatch(cmn_ctx->base); // check cbuf // get first message from buffer and begin the processing. if(cbuf_list_queue_len(&ctrl_hdl.c_main_buf_head)) { // Get the first message b = cbuf_list_dequeue(&ctrl_hdl.c_main_buf_head); } if (!of_hdr_valid(b->data)) { c_log_err("%s: Corrupted header", FN); return 0; /* Close the socket */ } // No need to allocate the worker thread // Pass the main_ctx now. Previously, this was: // sw = of_switch_alloc(c_wrk_ctx); sw = of_switch_alloc(main_ctx); of_switch_recv_msg(sw, b); return 0; }
int c_service_timed_throw_resp(mul_service_t *service) { char buf[1024]; int ret = 0; if (service->conn.dead) { return -1; } usleep(50000); /* 50 ms for any response */ c_make_socket_nonblocking(service->conn.fd); ret = recv(service->conn.fd, buf, 1024, MSG_DONTWAIT); if (c_recvd_sock_dead(ret)) { c_log_debug("Service(%s) connection Broken..\n", service->service_name); perror("c_service_timed_throw_resp"); c_service_reconnect(service); ret = -1; } else { c_make_socket_blocking(service->conn.fd); } /* return > 0 if we got a response else < 0 */ return ret; }
/** * mul_route_service_get - * * Get a handle to the routing service */ void * mul_route_service_get(void) { void *ptr = NULL; rt_apsp_t *rt_apsp_info; int serv_fd; c_log_debug("%s: ", FN); if ((serv_fd = shm_open(MUL_TR_SERVICE_NAME, O_RDONLY, 0)) < 0) { c_log_err("%s: Cant get service (unavailable)", FN); return NULL; } perror("shm_open"); ptr = mmap(0, RT_APSP_BLOCK_SIZE, PROT_READ, MAP_SHARED, serv_fd, 0); if (ptr == MAP_FAILED) { c_log_err("%s: Cant get service (failed to map)", FN); return NULL; } rt_apsp_info = calloc(1, sizeof(*rt_apsp_info)); if (!rt_apsp_info) { c_log_err("%s: RT apsp info allocation fail", FN); return NULL; } mul_route_init_block_meta(rt_apsp_info, ptr); close(serv_fd); return (void *)rt_apsp_info; }
/* * __c_service_wait_response - * * This is not yet smp proof */ struct cbuf * __c_service_wait_response(mul_service_t *service, int *ret) { struct pollfd pfd; if (service->conn.dead) return NULL; service->conn.cbuf = NULL; pfd.fd = service->conn.fd; pfd.events = POLLIN; *ret = poll(&pfd, 1, C_SERV_MSG_TIMEO_MS); if (*ret == -1) { perror("poll"); c_service_reconnect(service); return NULL; } else if (*ret == 0) { c_log_debug("%s:Timeout!", FN); } else { if (pfd.revents & POLLIN) { *ret = c_socket_read_block_loop(service->conn.fd, service, &service->conn, C_SERV_RCV_BUF_SZ, (conn_proc_t)(c_service_fetch_response), of_get_data_len, of_hdr_valid, sizeof(struct ofp_header)); if (c_recvd_sock_dead(*ret)) { c_log_debug("Service(%s) connection Broken..\n", service->service_name); perror("c_service_wait_repsonse"); if (service->conn.cbuf) { free_cbuf(service->conn.cbuf); service->conn.cbuf = NULL; } c_service_reconnect(service); return NULL; } return service->conn.cbuf; } return NULL; } /* NOT Reached */ return NULL; }
/** * mul_route_list_dump - * */ static void mul_route_list_dump(rt_list_t *path_head) { rt_list_t *cur_path = path_head; int i = 0; char *dump_route; while (cur_path) { c_log_debug("List # %d", i++); dump_route = mul_dump_route(cur_path->route); c_log_err(" %s", dump_route); if (dump_route) free(dump_route); cur_path = cur_path->next; } }
/** * @name c_app_reconn_timer * @brief Timer which is invoked once main controller connection goes down * and it retries till connection is restored */ static void c_app_reconn_timer(evutil_socket_t fd UNUSED, short event UNUSED, void *arg) { c_app_hdl_t *hdl = arg; struct timeval tv = { 2, 0 }; if(!c_app_sock_init(hdl, server)) { c_log_debug("Connection to controller restored"); event_del((struct event *)(hdl->reconn_timer_event)); event_free((struct event *)(hdl->reconn_timer_event)); c_app_notify_reconnect(hdl); return; } evtimer_add(hdl->reconn_timer_event, &tv); }
/** * @name c_app_read * @brief Reads on controller connection socket */ static void c_app_read(evutil_socket_t fd, short events UNUSED, void *arg) { c_app_hdl_t *hdl = arg; int ret; ret = c_socket_read_nonblock_loop(fd, hdl, &hdl->conn, C_APP_RCV_BUF_SZ, (conn_proc_t)c_app_recv_msg, of_get_data_len, of_hdr_valid, sizeof(struct ofp_header)); if (c_recvd_sock_dead(ret)) { c_log_debug("Controller connection Lost..\n"); perror("c_app_read"); c_app_reconnect(hdl); } return; }
/** * mul_loop_port_mod - * * Get port flags corresponding to loop state * and apply them */ void mul_loop_port_mod(uint64_t dpid, uint32_t port_no, int loop_state) { uint32_t config = 0; uint32_t mask = 0; switch (loop_state) { case LOOP_PORT_STATUS_INIT: config |= OFPPC_NO_RECV| OFPPC_NO_FLOOD; mask |= (OFPPC_NO_RECV|OFPPC_NO_FWD|OFPPC_NO_FLOOD); //c_log_debug("|Loop| Init link %d on 0x%llx", port_no, dpid); mul_dp_port_update(dpid, port_no, config, mask); break; case LOOP_PORT_STATUS_NONE: mask |= (OFPPC_NO_RECV|OFPPC_NO_FWD|OFPPC_NO_FLOOD); //c_log_debug("|Loop| link %d on 0x%llx None ", port_no, dpid); mul_dp_port_update(dpid, port_no, config, mask); break; case LOOP_PORT_STATUS_DP: case LOOP_PORT_STATUS_RP: mask |= (OFPPC_NO_RECV|OFPPC_NO_FWD|OFPPC_NO_FLOOD); //c_log_debug("|Loop| Turning on link %d on 0x%llx", port_no, dpid); mul_dp_port_update(dpid, port_no, config, mask); break; case LOOP_PORT_STATUS_DP_N: config |= (OFPPC_NO_RECV|OFPPC_NO_FLOOD); mask |= (OFPPC_NO_RECV|OFPPC_NO_FLOOD); //c_log_debug("|Loop| Turning on link %d on 0x%llx", port_no, dpid); mul_dp_port_update(dpid, port_no, config, mask); break; case LOOP_PORT_STATUS_NDP: config |= (OFPPC_NO_RECV|OFPPC_NO_FLOOD); mask |= (OFPPC_NO_RECV|OFPPC_NO_FLOOD); mul_dp_port_update(dpid, port_no, config, mask); c_log_debug("|Loop| Turning off link %d on 0x%llx", port_no, U642ULL(dpid)); break; default: break; } }
/** * @name fab_host_unref * @brief Remove a reference to a host * @param [in] host : Host pointer * * @retval void Nothing */ static void fab_host_unref(fab_host_t *host) { fab_tenant_net_t *ten_nw = host->tenant_nw; if (!atomic_read(&host->ref)) { c_log_debug("%s: Host Destroyed (TNID %u: ip(0x%x) " "mac(%02x:%02x:%02x:%02x:%02x:%02x:", FN, host->hkey.tn_id, host->hkey.host_ip, host->hkey.host_mac[0], host->hkey.host_mac[1], host->hkey.host_mac[2], host->hkey.host_mac[3], host->hkey.host_mac[4], host->hkey.host_mac[5]); if (ten_nw) { ten_nw->host_list = g_slist_remove(ten_nw->host_list, host); fab_tenant_nw_put(ten_nw); } fab_free(host); } else { atomic_dec(&host->ref, 1); } }
static void c_service_accept(evutil_socket_t listener, short event UNUSED, void *arg) { mul_service_t *service = arg; struct sockaddr_storage ss; socklen_t slen = sizeof(ss); int fd = accept(listener, (struct sockaddr*)&ss, &slen); mul_service_t *serv_inst = NULL; c_log_debug("%s:", FN); if (fd < 0) { perror("accept"); } else if (fd > FD_SETSIZE) { close(fd); } else { serv_inst = calloc(1, sizeof(mul_service_t)); assert(serv_inst); memcpy(serv_inst, service, sizeof(*serv_inst)); memset(&serv_inst->conn, 0, sizeof(c_conn_t)); c_make_socket_nonblocking(fd); serv_inst->conn.fd = fd; serv_inst->conn.rd_event = event_new(serv_inst->ev_base, serv_inst->conn.fd, EV_READ|EV_PERSIST, c_service_read, serv_inst); serv_inst->conn.wr_event = event_new(serv_inst->ev_base, serv_inst->conn.fd, EV_WRITE, //|EV_PERSIST, c_service_write_event, &serv_inst->conn); event_add((struct event *)(serv_inst->conn.rd_event), NULL); } }
static int c_thread_event_loop(struct c_cmn_ctx *cmn_ctx) { c_log_debug("%s: tid(%u)", __FUNCTION__, (unsigned int)pthread_self()); return event_base_dispatch(cmn_ctx->base); }
// This is where the switch threads get created. static int c_main_thread_final_init(struct c_main_ctx *m_ctx) { evutil_socket_t c_listener; struct c_worker_ctx *w_ctx, **w_ctx_slot; struct c_vty_ctx *vty_ctx; struct c_app_ctx *app_ctx, **app_ctx_slot; char ipc_path_str[64]; int thread_idx; ctrl_hdl_t *ctrl_hdl = m_ctx->cmn_ctx.c_hdl; struct thread_alloc_args t_args = { 0, 0, THREAD_WORKER, 0, m_ctx->cmn_ctx.c_hdl }; // Kajal: No event handling //m_ctx->cmn_ctx.base = event_base_new(); //assert(m_ctx->cmn_ctx.base); // m_ctx->worker_pool = calloc(m_ctx->nthreads, sizeof(void *)); // assert(m_ctx->worker_pool); /* // Worker thread creation for (thread_idx = 0; thread_idx < m_ctx->nthreads; thread_idx++) { // Indexing the worker thread in the main_ctx w_ctx_slot = c_tid_to_ctx_slot(m_ctx, thread_idx); t_args.thread_idx = thread_idx; w_ctx = c_alloc_thread_ctx(&t_args); assert(w_ctx); *w_ctx_slot = w_ctx; // Create Named pipes for IPC communication with main thread memset(ipc_path_str, 0, sizeof(ipc_path_str)); snprintf(ipc_path_str, 63, "%s%d", C_IPC_PATH, thread_idx); if (mkfifo(ipc_path_str, S_IRUSR | S_IWUSR | S_IWGRP) == -1 && errno != EEXIST) { perror(""); assert(0); } // Worker context is created for each thread, but, we will not // be using it. pthread_create(&w_ctx->cmn_ctx.thread, NULL, c_thread_main, w_ctx); // Save the fd for the named pipes in the worker context // Main ctx has the w_ctx saved. So main thread knows which is the // fd on which the IPC communication needs to begin w_ctx->main_wrk_conn.conn_type = C_CONN_TYPE_FILE; w_ctx->main_wrk_conn.fd = open(ipc_path_str, O_WRONLY); assert(w_ctx->main_wrk_conn.fd > 0); // Worker thread context is saved in the main_ctx and the controller handle ctrl_hdl->worker_ctx_list[thread_idx] = (void *)w_ctx; } // Switch listener // Kajal: No need to create a socket to listen on. // Library will use the callback defined in MUL to do processing. c_listener = c_server_socket_create(INADDR_ANY, C_LISTEN_PORT); assert(c_listener > 0); m_ctx->c_accept_event = event_new(m_ctx->cmn_ctx.base, -1, EV_READ|EV_PERSIST, cc_of_new_conn_event_handler, (void*)m_ctx); event_add(m_ctx->c_accept_event, NULL); */ m_ctx->app_pool = calloc(m_ctx->n_appthreads, sizeof(void *)); assert(m_ctx->app_pool); /* Application thread creation */ for (thread_idx = 0; thread_idx < m_ctx->n_appthreads; thread_idx++) { app_ctx_slot = c_tid_to_app_ctx_slot(m_ctx, thread_idx); t_args.thread_type = THREAD_APP; t_args.thread_idx = thread_idx; app_ctx = c_alloc_thread_ctx(&t_args); assert(app_ctx); *app_ctx_slot = app_ctx; memset(ipc_path_str, 0, sizeof(ipc_path_str)); snprintf(ipc_path_str, 63, "%s%d", C_IPC_APP_PATH, thread_idx); if (mkfifo(ipc_path_str, S_IRUSR | S_IWUSR | S_IWGRP) == -1 && errno != EEXIST) { perror(""); assert(0); } pthread_create(&app_ctx->cmn_ctx.thread, NULL, c_thread_main, app_ctx); app_ctx->main_wrk_conn.conn_type = C_CONN_TYPE_FILE; app_ctx->main_wrk_conn.fd = open(ipc_path_str, O_WRONLY); assert(app_ctx->main_wrk_conn.fd > 0); } /* VTY thread creation */ t_args.thread_type = THREAD_VTY; vty_ctx = c_alloc_thread_ctx(&t_args); assert(vty_ctx); pthread_create(&vty_ctx->cmn_ctx.thread, NULL, c_thread_main, vty_ctx); /* Application listener */ c_listener = c_server_socket_create(INADDR_ANY, C_APP_LISTEN_PORT); assert(c_listener); m_ctx->c_app_accept_event = event_new(m_ctx->cmn_ctx.base, c_listener, EV_READ|EV_PERSIST, c_app_accept, (void*)m_ctx); event_add(m_ctx->c_app_accept_event, NULL); c_listener = c_server_socket_create(INADDR_ANY, C_APP_AUX_LISTEN_PORT); assert(c_listener); m_ctx->c_app_aux_accept_event= event_new(m_ctx->cmn_ctx.base, c_listener, EV_READ|EV_PERSIST, c_aux_app_accept, (void*)m_ctx); event_add(m_ctx->c_app_aux_accept_event, NULL); m_ctx->cmn_ctx.run_state = THREAD_STATE_RUNNING; c_set_thread_dfl_affinity(); c_log_debug("%s: running tid(%u)", __FUNCTION__, (unsigned int)pthread_self()); return 0; }
static int c_main_thread_final_init(struct c_main_ctx *m_ctx) { evutil_socket_t c_listener; struct c_worker_ctx *w_ctx, **w_ctx_slot; struct c_vty_ctx *vty_ctx; struct c_app_ctx *app_ctx, **app_ctx_slot; char ipc_path_str[64]; int thread_idx; ctrl_hdl_t *ctrl_hdl = m_ctx->cmn_ctx.c_hdl; struct thread_alloc_args t_args = { 0, 0, THREAD_WORKER, 0, m_ctx->cmn_ctx.c_hdl }; m_ctx->cmn_ctx.base = event_base_new(); assert(m_ctx->cmn_ctx.base); m_ctx->worker_pool = calloc(m_ctx->nthreads, sizeof(void *)); assert(m_ctx->worker_pool); m_ctx->app_pool = calloc(m_ctx->n_appthreads, sizeof(void *)); assert(m_ctx->app_pool); /* Worker thread creation */ for (thread_idx = 0; thread_idx < m_ctx->nthreads; thread_idx++) { w_ctx_slot = c_tid_to_ctx_slot(m_ctx, thread_idx); t_args.thread_idx = thread_idx; w_ctx = c_alloc_thread_ctx(&t_args); assert(w_ctx); *w_ctx_slot = w_ctx; memset(ipc_path_str, 0, sizeof(ipc_path_str)); snprintf(ipc_path_str, 63, "%s%d", C_IPC_PATH, thread_idx); if (mkfifo(ipc_path_str, S_IRUSR | S_IWUSR | S_IWGRP) == -1 && errno != EEXIST) { perror(""); assert(0); } pthread_create(&w_ctx->cmn_ctx.thread, NULL, c_thread_main, w_ctx); w_ctx->main_wrk_conn.conn_type = C_CONN_TYPE_FILE; w_ctx->main_wrk_conn.fd = open(ipc_path_str, O_WRONLY); assert(w_ctx->main_wrk_conn.fd > 0); ctrl_hdl->worker_ctx_list[thread_idx] = (void *)w_ctx; } /* Application thread creation */ for (thread_idx = 0; thread_idx < m_ctx->n_appthreads; thread_idx++) { app_ctx_slot = c_tid_to_app_ctx_slot(m_ctx, thread_idx); t_args.thread_type = THREAD_APP; t_args.thread_idx = thread_idx; app_ctx = c_alloc_thread_ctx(&t_args); assert(app_ctx); *app_ctx_slot = app_ctx; memset(ipc_path_str, 0, sizeof(ipc_path_str)); snprintf(ipc_path_str, 63, "%s%d", C_IPC_APP_PATH, thread_idx); if (mkfifo(ipc_path_str, S_IRUSR | S_IWUSR | S_IWGRP) == -1 && errno != EEXIST) { perror(""); assert(0); } pthread_create(&app_ctx->cmn_ctx.thread, NULL, c_thread_main, app_ctx); app_ctx->main_wrk_conn.conn_type = C_CONN_TYPE_FILE; app_ctx->main_wrk_conn.fd = open(ipc_path_str, O_WRONLY); assert(app_ctx->main_wrk_conn.fd > 0); } /* VTY thread creation */ t_args.thread_type = THREAD_VTY; vty_ctx = c_alloc_thread_ctx(&t_args); assert(vty_ctx); pthread_create(&vty_ctx->cmn_ctx.thread, NULL, c_thread_main, vty_ctx); /* Switch listener */ c_listener = c_server_socket_create(INADDR_ANY, ctrl_hdl->c_port); assert(c_listener > 0); m_ctx->c_accept_event = event_new(m_ctx->cmn_ctx.base, c_listener, EV_READ|EV_PERSIST, c_accept, (void*)m_ctx); event_add(m_ctx->c_accept_event, NULL); /* Application listener */ c_listener = c_server_socket_create(INADDR_ANY, C_APP_LISTEN_PORT); assert(c_listener); m_ctx->c_app_accept_event = event_new(m_ctx->cmn_ctx.base, c_listener, EV_READ|EV_PERSIST, c_app_accept, (void*)m_ctx); event_add(m_ctx->c_app_accept_event, NULL); c_listener = c_server_socket_create(INADDR_ANY, C_APP_AUX_LISTEN_PORT); assert(c_listener); m_ctx->c_app_aux_accept_event= event_new(m_ctx->cmn_ctx.base, c_listener, EV_READ|EV_PERSIST, c_aux_app_accept, (void*)m_ctx); event_add(m_ctx->c_app_aux_accept_event, NULL); m_ctx->cmn_ctx.run_state = THREAD_STATE_RUNNING; c_set_thread_dfl_affinity(); c_log_debug("%s: running tid(%u)", __FUNCTION__, (unsigned int)pthread_self()); return 0; }
/** * hello_sw_del - * Switch delete event notifier * * @sw : Switch arg passed by infra layer * @return : void */ static void hello_sw_del(mul_switch_t *sw) { c_log_debug("switch dpid 0x%llx left network", (unsigned long long)(sw->dpid)); }
/** * @name mul_conx_mod_uflow - * @brief Add/Del a conx association for a user-supplied flow flow * @param [in] add flag to specify add or del operation * @param [in] n_dpid number of source dpids requested * @param [in] src_dps list of source dpids * @param [in] dst_dp Destination dpid * @param [in] in_fl User-flow to match in incoming (source) switches * @param [in] tunnel_key Tunnel-id if connection between src and dest is overlay * @param [in] tunnel_type Tunnel-type (undefined for now) * @param [in] actions Actions to be applied at the egress node * @param [in] action_len Length of the actions * @param [in] fl_flags Flow flags using which flow is to be installed in the core * @param [in] conx_flags Mask of following flags - * CONX_UFLOW_FORCE: Force to add all path-flows irrespective of * failure in any path * CONX_UFLOW_DFL: Install the user flow with default low priority * * @retval int 0 for success or non-0 for failure */ int mul_conx_mod_uflow(void *service, bool add, size_t n_dpid, uint64_t *src_dps, uint64_t dst_dp, struct flow *in_fl, struct flow *in_mask, uint32_t tunnel_key, uint32_t tunnel_type, void *actions, size_t action_len, uint64_t fl_flags, uint32_t conx_flags) { struct cbuf *b; struct c_ofp_auxapp_cmd *cofp_auc; struct c_conx_user_flow *conx_fl; int ret = 0; int i = 0; uint8_t zero_mac[6] = { 0, 0, 0, 0, 0, 0}; size_t ext_len = 0; uint8_t *act_ptr = NULL; uint64_t *src_dpid; if (!service || n_dpid < 1 || n_dpid > 1024) return -1; if (tunnel_type == CONX_TUNNEL_OF && (memcmp(in_mask->dl_dst, zero_mac, 6) || memcmp(in_mask->dl_src, zero_mac, 6))) { c_log_err("uFlow can't use src-dst Mac match"); return -1; } if (of_check_flow_wildcard_generic(in_fl, in_mask)) { c_log_debug("Conx add-uflow all-wc not allowed"); return -1; } ext_len = action_len + (sizeof(uint64_t)*n_dpid); b = of_prep_msg(sizeof(*cofp_auc) + sizeof(*conx_fl) + ext_len, C_OFPT_AUX_CMD, 0); if (!b) return -1; cofp_auc = CBUF_DATA(b); cofp_auc->cmd_code = add ? htonl(C_AUX_CMD_CONX_ADD_UFLOW): htonl(C_AUX_CMD_CONX_DEL_UFLOW); conx_fl = ASSIGN_PTR(cofp_auc->data); conx_fl->dst_dpid = htonll(dst_dp); conx_fl->tunnel_key = htonl(tunnel_key); /* Overridden as tenant-id */ conx_fl->tunnel_type = htonl(tunnel_type); conx_fl->app_cookie = htonl(c_app_main_hdl.app_cookie); conx_fl->fl_flags = htonll(fl_flags); conx_fl->conx_flags = htonl(conx_flags); conx_fl->n_src = htonll(n_dpid); memcpy(&conx_fl->flow, in_fl, sizeof(struct flow)); memcpy(&conx_fl->mask, in_mask, sizeof(struct flow)); src_dpid = ASSIGN_PTR(conx_fl->src_dpid_list); for (i = 0; i < n_dpid; i++) { src_dpid[i] = htonll(src_dps[i]); } if (add && action_len) { act_ptr = INC_PTR8(conx_fl->src_dpid_list, sizeof(uint64_t)*n_dpid); memcpy(act_ptr, actions, action_len); } c_service_send(service, b); if (!(fl_flags & C_FL_NO_ACK)) { b = c_service_wait_response(service); if (b) { cofp_auc = CBUF_DATA(b); if (!c_check_reply_type(b, C_AUX_CMD_SUCCESS)) { ret = 0; } free_cbuf(b); } } return ret; }