1 #include <stdio.h> 2 3 #include <stdint.h> 4 #include <stdlib.h> 5 #include <unistd.h> 6 #include <assert.h> 7 8 #ifdef DARWIN 9 #include <netinet/if_ether.h> 10 #include <netinet/tcp.h> 11 #else 12 #include <linux/if_ether.h> 13 #include <linux/tcp.h> 14 #endif 15 #include <string.h> 16 #include <netinet/ip.h> 17 18 #include "mtcp.h" 19 #include "arp.h" 20 #include "eth_out.h" 21 #include "debug.h" 22 #include "mos_api.h" 23 #include "config.h" 24 25 #ifndef TRUE 26 #define TRUE (1) 27 #endif 28 29 #ifndef FALSE 30 #define FALSE (0) 31 #endif 32 33 #ifndef ERROR 34 #define ERROR (-1) 35 #endif 36 37 #define MAX(a, b) ((a)>(b)?(a):(b)) 38 #define MIN(a, b) ((a)<(b)?(a):(b)) 39 40 #define MAX_WINDOW_SIZE 65535 41 42 /*----------------------------------------------------------------------------*/ 43 enum ETH_BUFFER_RETURN {BUF_RET_MAYBE, BUF_RET_ALWAYS}; 44 /*----------------------------------------------------------------------------*/ 45 #if !(E_PSIO || USE_CHUNK_BUF) 46 /* XXX - ignored at the moment (this is disabled by default) */ 47 inline void 48 InitWriteChunks(struct ps_handle* handle, struct ps_chunk *w_chunk) 49 { 50 int i, ret; 51 for (i = 0; i < ETH_NUM; i++) 52 { 53 ret = ps_alloc_chunk(handle, &w_chunk[i]); 54 if (ret != 0) 55 { 56 perror("ps_alloc_chunk"); 57 exit(1); 58 } 59 w_chunk[i].queue.ifindex = i; 60 w_chunk[i].recv_blocking = 0; 61 w_chunk[i].cnt = 0; 62 } 63 } 64 /*----------------------------------------------------------------------------*/ 65 int 66 FlushWriteBuffer(struct mtcp_thread_context* ctx, int ifidx) 67 { 68 int ret = 0; 69 struct ps_chunk* w_chunk = ctx->w_chunk; 70 mtcp_manager_t mtcp = ctx->mtcp_manager; 71 int i; 72 int drop = 0; 73 assert(ctx != NULL); 74 assert(w_chunk != NULL); 75 76 if (w_chunk[ifidx].cnt > 0) { 77 78 STAT_COUNT(mtcp->runstat.rounds_tx_try); 79 80 ret = ps_send_chunk(ctx->handle, &w_chunk[ifidx]); 81 drop = ctx->w_chunk[ifidx].cnt - ret; 82 83 if (ret < 0) { 84 TRACE_ERROR("ps_send_chunk failed to send chunks, %d:%d\n", 85 ifidx, w_chunk[ifidx].cnt); 86 return ret; 87 } else { 88 #ifdef NETSTAT 89 mtcp->nstat.tx_packets[ifidx] += ret; 90 #endif /* NETSTAT */ 91 92 for (i = 0; i < ret; i++) { 93 #ifdef PKTDUMP 94 DumpPacket(mtcp, 95 w_chunk[ifidx].buf + w_chunk[ifidx].info[i].offset, 96 w_chunk[ifidx].info[i].len, "OUT", ifidx); 97 #endif /* PKTDUMP */ 98 99 #ifdef NETSTAT 100 mtcp->nstat.tx_bytes[ifidx] += w_chunk[ifidx].info[i].len + 24; 101 #endif /* NETSTAT */ 102 } 103 104 #ifdef NETSTAT 105 if (ret != w_chunk[ifidx].cnt) { 106 mtcp->nstat.tx_drops[ifidx] += (w_chunk[ifidx].cnt - ret); 107 } 108 #endif /* NETSTAT */ 109 110 if (ret == 0) { 111 return ret; 112 } 113 } 114 115 #ifdef PKTDUMP 116 thread_printf(mtcp, mtcp->log_fp, "sent chunks, ret: %d (tries: %d)\n", 117 ret, w_chunk[ifidx].cnt); 118 thread_printf(mtcp, mtcp->log_fp, "======================================" 119 "======================================================" 120 "====================\n\n"); 121 #endif /* PKTDUMP */ 122 123 if (drop > 0) { 124 ctx->w_chunk[ifidx].cnt = drop; 125 for (i = 0; i < drop; i++) { 126 ctx->w_chunk[ifidx].info[i].len = 127 ctx->w_chunk[ifidx].info[ret + i].len; 128 ctx->w_chunk[ifidx].info[i].offset = 129 ctx->w_chunk[ifidx].info[ret + i].offset; 130 } 131 ctx->w_off[ifidx] = ctx->w_chunk[ifidx].info[drop - 1].offset + 132 (ctx->w_chunk[ifidx].info[drop - 1].len + 63) / 64 * 64; 133 ctx->w_cur_idx[ifidx] += ret; 134 } else { 135 ctx->w_chunk[ifidx].cnt = 0; 136 ctx->w_off[ifidx] = 0; 137 ctx->w_cur_idx[ifidx] = 0; 138 } 139 140 } 141 142 return ret; 143 } 144 /*----------------------------------------------------------------------------*/ 145 static inline char * 146 GetWriteBuffer(struct mtcp_thread_context *ctx, int method, int ifidx, int len) 147 { 148 struct ps_chunk *w_chunk = ctx->w_chunk; 149 uint32_t *w_off = ctx->w_off; 150 int w_idx; 151 152 assert(w_chunk != NULL); 153 assert(w_off != NULL); 154 155 if (ifidx < 0 || ifidx >= g_config.mos->netdev_table->num ) 156 return NULL; 157 158 //pthread_mutex_lock(&ctx->send_lock); 159 160 if (ctx->w_cur_idx[ifidx] + w_chunk[ifidx].cnt >= MAX_SEND_PCK_CHUNK) { 161 if (method == BUF_RET_MAYBE) { 162 return NULL; 163 } else if (method == BUF_RET_ALWAYS) { 164 if (FlushWriteBuffer(ctx, ifidx) <= 0) 165 return NULL; 166 } else { 167 assert(0); 168 } 169 } 170 171 assert(ctx->w_cur_idx[ifidx] + w_chunk[ifidx].cnt < MAX_SEND_PCK_CHUNK); 172 assert(w_off[ifidx] < MAX_PACKET_SIZE * MAX_CHUNK_SIZE); 173 174 w_idx = w_chunk[ifidx].cnt++; 175 w_chunk[ifidx].info[w_idx].len = len; 176 w_chunk[ifidx].info[w_idx].offset = w_off[ifidx]; 177 w_off[ifidx] += (len + 63) / 64 * 64; 178 179 //pthread_mutex_unlock(&ctx->send_lock); 180 181 return (w_chunk[ifidx].buf + w_chunk[ifidx].info[w_idx].offset); 182 } 183 /*----------------------------------------------------------------------------*/ 184 #else /* E_PSIO */ 185 int 186 FlushSendChunkBuf(mtcp_manager_t mtcp, int nif) 187 { 188 return 0; 189 } 190 #endif /* E_PSIO */ 191 /*----------------------------------------------------------------------------*/ 192 inline void 193 FillOutPacketEthContext(struct pkt_ctx *pctx, uint32_t cur_ts, int out_ifidx, 194 struct ethhdr *ethh, int eth_len) 195 { 196 pctx->p.cur_ts = cur_ts; 197 pctx->in_ifidx = -1; 198 pctx->out_ifidx = out_ifidx; 199 pctx->p.ethh = ethh; 200 pctx->p.eth_len = eth_len; 201 } 202 /*----------------------------------------------------------------------------*/ 203 uint8_t * 204 EthernetOutput(struct mtcp_manager *mtcp, struct pkt_ctx *pctx, 205 uint16_t h_proto, int nif, unsigned char* dst_haddr, uint16_t iplen, 206 uint32_t cur_ts) 207 { 208 uint8_t *buf; 209 struct ethhdr *ethh; 210 int i; 211 #if E_PSIO || USE_CHUNK_BUF 212 /* 213 * -sanity check- 214 * return early if no interface is set (if routing entry does not exist) 215 */ 216 if (nif < 0) { 217 TRACE_INFO("No interface set!\n"); 218 return NULL; 219 } 220 if (!mtcp->iom->get_wptr) { 221 TRACE_INFO("get_wptr() in io_module is undefined."); 222 return NULL; 223 } 224 buf = mtcp->iom->get_wptr(mtcp->ctx, nif, iplen + ETHERNET_HEADER_LEN); 225 #else 226 buf = GetWriteBuffer(mtcp->ctx, 227 BUF_RET_MAYBE, nif, iplen + ETHERNET_HEADER_LEN); 228 #endif 229 if (!buf) { 230 TRACE_DBG("Failed to get available write buffer\n"); 231 return NULL; 232 } 233 234 ethh = (struct ethhdr *)buf; 235 for (i = 0; i < ETH_ALEN; i++) { 236 ethh->h_source[i] = g_config.mos->netdev_table->ent[nif]->haddr[i]; 237 ethh->h_dest[i] = dst_haddr[i]; 238 } 239 ethh->h_proto = htons(h_proto); 240 241 if (pctx) 242 FillOutPacketEthContext(pctx, cur_ts, nif, 243 ethh, iplen + ETHERNET_HEADER_LEN); 244 245 return (uint8_t *)(ethh + 1); 246 } 247 /*----------------------------------------------------------------------------*/ 248 void 249 ForwardEthernetFrame(struct mtcp_manager *mtcp, struct pkt_ctx *pctx) 250 { 251 uint8_t *buf; 252 253 if (g_config.mos->nic_forward_table != NULL) { 254 pctx->out_ifidx = 255 g_config.mos->nic_forward_table->nic_fwd_table[pctx->in_ifidx]; 256 257 if (pctx->out_ifidx == -1) { 258 TRACE_DBG("Could not find outgoing index (index)!\n"); 259 return; 260 } 261 262 if (!mtcp->iom->get_wptr) { 263 TRACE_INFO("get_wptr() in io_module is undefined."); 264 return; 265 } 266 267 buf = mtcp->iom->get_wptr(mtcp->ctx, pctx->out_ifidx, pctx->p.eth_len); 268 269 if (!buf) { 270 TRACE_DBG("Failed to get available write buffer\n"); 271 return; 272 } 273 274 memcpy(buf, pctx->p.ethh, pctx->p.eth_len); 275 } else { 276 TRACE_DBG("Ethernet forwarding table entry does not exist.\n"); 277 } 278 } 279 /*----------------------------------------------------------------------------*/ 280