summaryrefslogtreecommitdiffstats
path: root/src/drivers/net/mlx_ipoib/ipoib.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/drivers/net/mlx_ipoib/ipoib.c')
-rw-r--r--src/drivers/net/mlx_ipoib/ipoib.c1027
1 files changed, 1027 insertions, 0 deletions
diff --git a/src/drivers/net/mlx_ipoib/ipoib.c b/src/drivers/net/mlx_ipoib/ipoib.c
new file mode 100644
index 000000000..85eaac7aa
--- /dev/null
+++ b/src/drivers/net/mlx_ipoib/ipoib.c
@@ -0,0 +1,1027 @@
+/*
+ This software is available to you under a choice of one of two
+ licenses. You may choose to be licensed under the terms of the GNU
+ General Public License (GPL) Version 2, available at
+ <http://www.fsf.org/copyleft/gpl.html>, or the OpenIB.org BSD
+ license, available in the LICENSE.TXT file accompanying this
+ software. These details are also available at
+ <http://openib.org/license.html>.
+
+ 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.
+
+ Copyright (c) 2004 Mellanox Technologies Ltd. All rights reserved.
+*/
+
+#include "ipoib.h"
+#include "ib_driver.h"
+#include "ib_mad.h"
+
+static const __u8 arp_packet_template[] = {
+ 0x00, 0x20, /* hardware type */
+ 0x08, 0x00, /* protocol type */
+ 20, /* hw size */
+ 4, /* protocol size */
+ 0x00, 0x00, /* opcode */
+
+ 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, /* sender's mac */
+ 0, 0, 0, 0, /* sender's IP address */
+
+ 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, /* Target's mac */
+ 0, 0, 0, 0 /* targets's IP address */
+};
+
+struct ipoib_data_st {
+ __u32 ipoib_qpn;
+ udqp_t ipoib_qph;
+ ud_av_t bcast_av;
+ cq_t snd_cqh;
+ cq_t rcv_cqh;
+ __u8 *port_gid_raw;
+} ipoib_data;
+
+#define NUM_MAC_ENTRIES (NUM_AVS+2)
+
+static struct mac_xlation_st mac_tbl[NUM_MAC_ENTRIES];
+static __u32 mac_counter = 1;
+static __u32 youth_counter = 0;
+
+#define EQUAL_GUIDS(g1, g2) ( \
+ ((g1)[0]==(g2)[0]) && \
+ ((g1)[1]==(g2)[1]) && \
+ ((g1)[2]==(g2)[2]) && \
+ ((g1)[3]==(g2)[3]) && \
+ ((g1)[4]==(g2)[4]) && \
+ ((g1)[5]==(g2)[5]) && \
+ ((g1)[6]==(g2)[6]) && \
+ ((g1)[7]==(g2)[7]) )
+
+#define MAC_IDX(i) (((mac_tbl[i].eth_mac_lsb[0])<<16) | \
+ ((mac_tbl[i].eth_mac_lsb[0])<<8) | \
+ (mac_tbl[i].eth_mac_lsb[0]))
+
+static inline const void *qpn2buf(__u32 qpn, const void *buf)
+{
+ ((__u8 *) buf)[0] = qpn >> 16;
+ ((__u8 *) buf)[1] = (qpn >> 8) & 0xff;
+ ((__u8 *) buf)[2] = qpn & 0xff;
+ return buf;
+}
+
+static inline __u32 buf2qpn(const void *buf)
+{
+ __u32 qpn;
+
+ qpn = ((((__u8 *) buf)[0]) << 16) +
+ ((((__u8 *) buf)[1]) << 8) + (((__u8 *) buf)[2]);
+
+ return qpn;
+}
+
+static int is_bcast_mac(const char *dest)
+{
+ int i;
+ __u8 mac = 0xff;
+
+ for (i = 0; i < 6; ++i)
+ mac &= dest[i];
+
+ return mac == 0xff;
+}
+
+/* find a free entry. if not found kick
+ * another entry.
+ */
+static int find_free_entry(void)
+{
+ __u32 youth = 0xffffffff;
+ __u8 i, remove_idx = NUM_MAC_ENTRIES;
+
+ /* find a free entry */
+ for (i = 0; i < NUM_MAC_ENTRIES; ++i) {
+ if (!mac_tbl[i].valid) {
+ mac_tbl[i].valid = 1;
+ mac_tbl[i].youth = youth_counter;
+ youth_counter++;
+ return i;
+ }
+ }
+
+ for (i = 0; i < NUM_MAC_ENTRIES; ++i) {
+ if ((mac_tbl[i].av == NULL) && (mac_tbl[i].youth < youth)) {
+ youth = mac_tbl[i].youth;
+ remove_idx = i;
+ }
+ }
+
+ if (remove_idx < NUM_MAC_ENTRIES) {
+ /* update the new youth value */
+ mac_tbl[remove_idx].youth = youth_counter;
+ youth_counter++;
+ return remove_idx;
+ } else {
+ tprintf("did not find an entry to kick");
+ return -1;
+ }
+}
+
+static int find_qpn_gid(__u32 qpn, const __u8 * gid)
+{
+ __u16 i;
+
+ for (i = 0; i < NUM_MAC_ENTRIES; ++i) {
+ if (mac_tbl[i].valid &&
+ (mac_tbl[i].qpn == qpn) &&
+ !memcmp(mac_tbl[i].gid.raw, gid, 16)) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+static void allocate_new_mac6(__u8 * mac_lsb)
+{
+ __u32 eth_counter;
+
+ eth_counter = mac_counter;
+ mac_counter = (mac_counter + 1) & 0xffffff;
+
+ mac_lsb[0] = eth_counter >> 16;
+ mac_lsb[1] = eth_counter >> 8;
+ mac_lsb[2] = eth_counter & 0xff;
+ tprintf("add mac: %x:%x:%x", mac_lsb[0], mac_lsb[1], mac_lsb[2]);
+}
+
+static void modify_arp_reply(__u8 * eth_mac_lsb, void *data)
+{
+ __u8 *packet;
+
+ /* skip 4 bytes */
+ packet = ((__u8 *) data) + 4;
+
+ /* modify hw type */
+ packet[0] = 0;
+ packet[1] = 1;
+
+ /* modify hw size */
+ packet[4] = 6;
+
+ /* modify sender's mac */
+ packet[8] = MLX_ETH_BYTE0;
+ packet[9] = MLX_ETH_BYTE1;
+ packet[10] = MLX_ETH_BYTE2;
+ packet[11] = eth_mac_lsb[0];
+ packet[12] = eth_mac_lsb[1];
+ packet[13] = eth_mac_lsb[2];
+
+ /* move sender's IP address */
+ memcpy(packet + 14, packet + 28, 4);
+
+ /* set target MAC - that's us */
+ packet[18] = MLX_ETH_BYTE0;
+ packet[19] = MLX_ETH_BYTE1;
+ packet[20] = MLX_ETH_BYTE2;
+ packet[21] = 0;
+ packet[22] = 0;
+ packet[23] = 0;
+
+ /* move target's IP address */
+ memcpy(packet + 24, packet + 52, 4);
+}
+
+static void modify_arp_request(__u8 * eth_mac_lsb, void *data)
+{
+ __u8 *packet;
+
+ /* skip 4 bytes */
+ packet = ((__u8 *) data) + 4;
+
+ /* modify hw type */
+ packet[0] = 0;
+ packet[1] = 1;
+
+ /* modify hw size */
+ packet[4] = 6;
+
+ /* modify sender's mac */
+ packet[8] = MLX_ETH_BYTE0;
+ packet[9] = MLX_ETH_BYTE1;
+ packet[10] = MLX_ETH_BYTE2;
+ packet[11] = eth_mac_lsb[0];
+ packet[12] = eth_mac_lsb[1];
+ packet[13] = eth_mac_lsb[2];
+
+ /* move sender's IP address */
+ memcpy(packet + 14, packet + 28, 4);
+
+ /* set target MAC - that's us */
+ packet[18] = 0;
+ packet[19] = 0;
+ packet[20] = 0;
+ packet[21] = 0;
+ packet[22] = 0;
+ packet[23] = 0;
+
+ /* move target's IP address */
+ memcpy(packet + 24, packet + 52, 4);
+}
+
+static int handle_arp_packet(void *buf, void **out_buf_p,
+ unsigned int *new_size_p)
+{
+ __u16 opcode;
+ const void *p;
+ const __u8 *gid;
+ __u32 qpn;
+ int idx;
+
+ opcode = get_opcode(buf);
+ switch (opcode) {
+ case ARP_OP_REQUESET:
+ case ARP_OP_REPLY:
+ break;
+
+ default:
+ return -1;
+ }
+
+ p = arp_mac20_get_sender_qpn(buf);
+ qpn = buf2qpn(p);
+ gid = arp_mac20_get_sender_gid(buf);
+
+ if (!memcmp(gid, get_port_gid(), 16)) {
+ /* my own gid */
+ *out_buf_p = NULL;
+ return 0;
+ }
+
+ idx = find_qpn_gid(qpn, gid);
+ if (idx == -1) {
+ /* entry not in the table */
+ idx = find_free_entry();
+ if (idx == -1) {
+ eprintf("we're in broch\n");
+ return -1;
+ }
+ allocate_new_mac6(mac_tbl[idx].eth_mac_lsb);
+ mac_tbl[idx].av = NULL; // free the av id it exists ?? !!
+ mac_tbl[idx].qpn = qpn;
+ memcpy(mac_tbl[idx].gid.raw, gid, 16);
+ }
+
+ if (opcode == ARP_OP_REQUESET) {
+ modify_arp_request(mac_tbl[idx].eth_mac_lsb, buf);
+ } else {
+ /* we want to filter possible broadcast arp
+ replies not directed to us */
+ p = arp_mac20_get_target_qpn(buf);
+ qpn = buf2qpn(p);
+ gid = arp_mac20_get_target_gid(buf);
+
+ if ((qpn != ipoib_data.ipoib_qpn) ||
+ (memcmp(gid, get_port_gid(), 16))) {
+ *out_buf_p = NULL;
+ return 0;
+ }
+
+ modify_arp_reply(mac_tbl[idx].eth_mac_lsb, buf);
+ {
+ __u8 i;
+ tprintf("arp reply dump:\n");
+ for (i = 4; i < 32; ++i) {
+ tprintf("%x: ", ((__u8 *) buf)[i]);
+ }
+ tprintf("\n");
+ }
+ }
+ *out_buf_p = ((__u8 *) buf) + 4;
+ *new_size_p = 28; /* size of eth arp packet */
+
+ tprintf("");
+
+ return 0;
+}
+
+static void modify_udp_csum(void *buf, __u16 size)
+{
+ __u8 *ptr = (__u8 *) buf;
+ __u32 csum = 0;
+ __u16 chksum;
+ __u16 buf_size;
+ __u16 *tmp;
+ int i;
+
+ buf_size = (size & 1) ? size + 1 : size;
+ tmp = (__u16 *) (ptr + 12); /* src and dst ip addresses */
+ for (i = 0; i < 4; ++i) {
+ csum += tmp[i];
+ }
+
+ csum += 0x1100; // udp protocol
+
+ tmp = (__u16 *) (ptr + 26);
+ tmp[0] = 0; /* zero the checksum */
+
+ tmp = (__u16 *) (ptr + 24);
+ csum += tmp[0];
+
+ tmp = (__u16 *) (ptr + 20);
+
+ for (i = 0; i < ((buf_size - 20) >> 1); ++i) {
+ csum += tmp[i];
+ }
+
+ chksum = ~((__u16) ((csum & 0xffff) + (csum >> 16)));
+
+ tmp = (__u16 *) (ptr + 26);
+ tmp[0] = chksum; /* set the checksum */
+}
+
+static void modify_dhcp_resp(void *buf, __u16 size)
+{
+ set_eth_hwtype(buf);
+ set_eth_hwlen(buf);
+ set_own_mac(buf);
+ modify_udp_csum(buf, size);
+}
+
+static void get_my_client_id(__u8 * my_client_id)
+{
+
+ my_client_id[0] = 0;
+ qpn2buf(ipoib_data.ipoib_qpn, my_client_id + 1);
+ memcpy(my_client_id + 4, ipoib_data.port_gid_raw, 16);
+}
+
+static const __u8 *get_client_id(const void *buf, int len)
+{
+ const __u8 *ptr;
+ int delta;
+
+ if (len < 268)
+ return NULL;
+
+ /* pointer to just after magic cookie */
+ ptr = (const __u8 *)buf + 268;
+
+ /* find last client identifier option */
+ do {
+ if (ptr[0] == 255) {
+ /* found end of options list */
+ return NULL;
+ }
+
+ if (ptr[0] == 0x3d) {
+ /* client identifer option */
+ return ptr + 3;
+ }
+
+ delta = ptr[1] + 2;
+ ptr += delta;
+ len -= delta;
+ } while (len > 0);
+
+ return NULL;
+}
+
+static int handle_ipv4_packet(void *buf, void **out_buf_p,
+ unsigned int *new_size_p, int *is_bcast_p)
+{
+ void *new_buf;
+ __u16 new_size;
+ __u8 msg_type;
+ __u8 my_client_id[20];
+
+ new_buf = (void *)(((__u8 *) buf) + 4);
+ new_size = (*new_size_p) - 4;
+ *out_buf_p = new_buf;
+ *new_size_p = new_size;
+
+ if (get_ip_protocl(new_buf) == IP_PROT_UDP) {
+ __u16 udp_dst_port;
+ const __u8 *client_id;
+
+ udp_dst_port = get_udp_dst_port(new_buf);
+
+ if (udp_dst_port == 67) {
+ /* filter dhcp requests */
+ *out_buf_p = 0;
+ return 0;
+ }
+
+ if (udp_dst_port == 68) {
+ get_my_client_id(my_client_id);
+
+ /* packet client id */
+ client_id = get_client_id(new_buf, new_size);
+ if (!client_id) {
+ *out_buf_p = 0;
+ return 0;
+ }
+
+ if (memcmp(client_id, my_client_id, 20)) {
+ *out_buf_p = 0;
+ return 0;
+ }
+ }
+ }
+
+ msg_type = get_dhcp_msg_type(new_buf);
+ if ((get_ip_protocl(new_buf) == IP_PROT_UDP) &&
+ (get_udp_dst_port(new_buf) == 68) &&
+ ((msg_type == DHCP_TYPE_RESPONSE) || (msg_type == DHCP_TYPE_ACK))
+ ) {
+ *is_bcast_p = 1;
+ modify_dhcp_resp(new_buf, new_size);
+ }
+
+ return 0;
+}
+
+static int is_valid_arp(void *buf, unsigned int size)
+{
+ __u8 *ptr = buf;
+ __u16 tmp;
+
+ if (size != 60) {
+ eprintf("");
+ return 0;
+ }
+ if (be16_to_cpu(*((__u16 *) ptr)) != ARP_PROT_TYPE)
+ return 0;
+
+ if (be16_to_cpu(*((__u16 *) (ptr + 4))) != IPOIB_HW_TYPE)
+ return 0;
+
+ if (be16_to_cpu(*((__u16 *) (ptr + 6))) != IPV4_PROT_TYPE)
+ return 0;
+
+ if (ptr[8] != 20) /* hw addr len */
+ return 0;
+
+ if (ptr[9] != 4) /* protocol len = 4 for IP */
+ return 0;
+
+ tmp = be16_to_cpu(*((__u16 *) (ptr + 10)));
+ if ((tmp != ARP_OP_REQUESET) && (tmp != ARP_OP_REPLY))
+ return 0;
+
+ return 1;
+}
+
+static int ipoib_handle_rcv(void *buf, void **out_buf_p,
+ unsigned int *new_size_p, int *is_bcast_p)
+{
+ __u16 prot_type;
+ int rc;
+
+ prot_type = get_prot_type(buf);
+ switch (prot_type) {
+ case ARP_PROT_TYPE:
+ tprintf("");
+ if (is_valid_arp(buf, *new_size_p)) {
+ tprintf("got valid arp");
+ rc = handle_arp_packet(buf, out_buf_p, new_size_p);
+ if (rc) {
+ eprintf("");
+ return rc;
+ }
+ if (!out_buf_p) {
+ tprintf("");
+ }
+ tprintf("arp for me");
+ *is_bcast_p = 1;
+ return rc;
+ } else {
+ tprintf("got invalid arp");
+ *out_buf_p = NULL;
+ return 0;
+ }
+
+ case IPV4_PROT_TYPE:
+ tprintf("");
+ rc = handle_ipv4_packet(buf, out_buf_p, new_size_p, is_bcast_p);
+ return rc;
+ }
+ eprintf("prot=0x%x", prot_type);
+ return -1;
+}
+
+static int is_null_mac(const __u8 * mac)
+{
+ __u8 i, tmp = 0;
+ __u8 lmac[6];
+
+ memcpy(lmac, mac, 6);
+
+ for (i = 0; i < 6; ++i) {
+ tmp |= lmac[i];
+ }
+
+ if (tmp == 0)
+ return 1;
+ else
+ return 0;
+}
+
+static int find_mac(const __u8 * mac)
+{
+ int i;
+ const __u8 *tmp = mac + 3;
+
+ for (i = 0; i < NUM_MAC_ENTRIES; ++i) {
+ tprintf("checking 0x%02x:0x%02x:0x%02x valid=%d",
+ mac_tbl[i].eth_mac_lsb[0], mac_tbl[i].eth_mac_lsb[1],
+ mac_tbl[i].eth_mac_lsb[2], mac_tbl[i].valid);
+ if (mac_tbl[i].valid && !memcmp(mac_tbl[i].eth_mac_lsb, tmp, 3))
+ return i;
+ }
+ tprintf("mac: %x:%x:%x - dumping", tmp[0], tmp[1], tmp[2]);
+ for (i = 0; i < NUM_MAC_ENTRIES; ++i) {
+ //__u8 *gid= mac_tbl[i].gid.raw;
+ //__u8 *m= mac_tbl[i].eth_mac_lsb;
+ /*if (mac_tbl[i].valid) {
+ tprintf("%d: qpn=0x%lx, "
+ "gid=%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x, "
+ "av=0x%lx, "
+ "youth= %ld, "
+ "mac=%x:%x:%x\n",
+ i, mac_tbl[i].qpn,
+ gid[0], gid[1], gid[2], gid[3], gid[4], gid[5], gid[6], gid[7],
+ gid[8], gid[9], gid[10], gid[11], gid[12], gid[13], gid[14], gid[15],
+ mac_tbl[i].av, mac_tbl[i].youth,
+ m[0], m[1], m[2]);
+ } */
+ }
+ return -1;
+}
+
+static int send_bcast_packet(__u16 protocol, const void *data, __u16 size)
+{
+ ud_send_wqe_t snd_wqe, tmp_wqe;
+ int rc;
+ int is_good;
+ void *send_buffer;
+
+ snd_wqe = alloc_send_wqe(ipoib_data.ipoib_qph);
+ if (!snd_wqe) {
+ eprintf("");
+ return -1;
+ }
+
+ send_buffer = get_send_wqe_buf(snd_wqe, 0);
+ *((__u32 *) send_buffer) = cpu_to_be32(protocol << 16);
+ prep_send_wqe_buf(ipoib_data.ipoib_qph, ipoib_data.bcast_av,
+ snd_wqe, data, 4, size, 0);
+
+ rc = post_send_req(ipoib_data.ipoib_qph, snd_wqe, 1);
+ if (rc) {
+ eprintf("");
+ goto ex;
+ }
+
+ rc = poll_cqe_tout(ipoib_data.snd_cqh, SEND_CQE_POLL_TOUT, &tmp_wqe,
+ &is_good);
+ if (rc) {
+ eprintf("");
+ goto ex;
+ }
+ if (!is_good) {
+ eprintf("");
+ rc = -1;
+ goto ex;
+ }
+ if (tmp_wqe != snd_wqe) {
+ eprintf("");
+ rc = -1;
+ goto ex;
+ }
+
+ ex:free_wqe(snd_wqe);
+ return rc;
+}
+
+static int send_ucast_packet(const __u8 * mac, __u16 protocol, const void *data,
+ __u16 size)
+{
+ ud_send_wqe_t snd_wqe, tmp_wqe;
+ ud_av_t av;
+ udqp_t qph;
+ __u16 dlid;
+ __u8 sl, rate;
+ int rc;
+ int i;
+ int is_good;
+
+ i = find_mac(mac);
+ if (i < 0) {
+ tprintf("");
+ return -1;
+ }
+
+ if (!mac_tbl[i].av) {
+ rc = get_path_record(&mac_tbl[i].gid, &dlid, &sl, &rate);
+ if (rc) {
+ eprintf("");
+ return -1;
+ } else {
+ tprintf("get_path_record() success dlid=0x%x", dlid);
+ }
+
+ /* no av - allocate one */
+ av = alloc_ud_av();
+ if (!av) {
+ eprintf("");
+ return -1;
+ }
+ modify_av_params(av, dlid, 1, sl, rate, &mac_tbl[i].gid,
+ mac_tbl[i].qpn);
+ mac_tbl[i].av = av;
+ } else {
+ av = mac_tbl[i].av;
+ }
+ qph = ipoib_data.ipoib_qph;
+ snd_wqe = alloc_send_wqe(qph);
+ if (!snd_wqe) {
+ eprintf("");
+ return -1;
+ }
+
+ *((__u32 *) get_send_wqe_buf(snd_wqe, 0)) = cpu_to_be32(protocol << 16);
+ prep_send_wqe_buf(qph, av, snd_wqe, data, 4, size, 0);
+
+ rc = post_send_req(qph, snd_wqe, 1);
+ if (rc) {
+ eprintf("");
+ return -1;
+ }
+
+ rc = poll_cqe_tout(ipoib_data.snd_cqh, SEND_CQE_POLL_TOUT, &tmp_wqe,
+ &is_good);
+ if (rc) {
+ eprintf("");
+ goto ex;
+ }
+ if (!is_good) {
+ eprintf("");
+ rc = -1;
+ goto ex;
+ }
+ if (tmp_wqe != snd_wqe) {
+ eprintf("");
+ rc = -1;
+ goto ex;
+ }
+
+ ex:free_wqe(snd_wqe);
+ return rc;
+}
+
+static void *alloc_convert_arp6_msg(const void *data,
+ struct arp_packet_st *ipoib_arp)
+{
+ void *buf;
+ const void *p1;
+ int idx;
+ __u8 qpn[3];
+
+ memcpy(ipoib_arp, arp_packet_template, sizeof arp_packet_template);
+ buf = ipoib_arp;
+
+ /* update opcode */
+ p1 = arp_mac6_get_opcode(data);
+ arp_mac20_set_opcode(p1, buf);
+
+ /* update sender ip */
+ p1 = arp_mac6_get_sender_ip(data);
+ arp_mac20_set_sender_ip(p1, buf);
+
+ /* update target ip */
+ p1 = arp_mac6_get_target_ip(data);
+ arp_mac20_set_target_ip(p1, buf);
+
+ /* update sender mac */
+ qpn2buf(ipoib_data.ipoib_qpn, qpn);
+ arp_mac20_set_sender_mac(qpn, ipoib_data.port_gid_raw, buf);
+
+ /* update target mac */
+ p1 = arp_mac6_get_target_mac(data);
+ if (!is_null_mac(p1)) {
+ idx = find_mac(p1);
+ if (idx == -1) {
+ __u8 *_ptr = (__u8 *) p1;
+ eprintf("could not find mac %x:%x:%x",
+ _ptr[3], _ptr[4], _ptr[5]);
+ return NULL;
+ }
+ qpn2buf(mac_tbl[idx].qpn, qpn);
+ arp_mac20_set_target_mac(qpn, mac_tbl[idx].gid.raw, buf);
+ }
+
+ return buf;
+}
+
+static __u16 set_client_id(__u8 * packet)
+{
+ __u8 *ptr;
+ __u8 y[3];
+ __u16 new_size;
+
+ /* pointer to just after magic cookie */
+ ptr = packet + 268;
+
+ /* find last option */
+ do {
+ if (ptr[0] == 255) {
+ /* found end of options list */
+ break;
+ }
+ ptr = ptr + ptr[1] + 2;
+ } while (1);
+
+ ptr[0] = 61; /* client id option identifier */
+ ptr[1] = 21; /* length of the option */
+ ptr[2] = IPOIB_HW_TYPE;
+ ptr[3] = 0;
+ qpn2buf(ipoib_data.ipoib_qpn, y);
+ memcpy(ptr + 4, y, 3);
+ memcpy(ptr + 7, ipoib_data.port_gid_raw, 16);
+ ptr[23] = 255;
+ new_size = (__u16) (ptr + 24 - packet);
+ if (new_size & 3) {
+ new_size += (4 - (new_size & 3));
+ }
+ return new_size;
+}
+
+static __u16 calc_udp_csum(__u8 * packet)
+{
+ __u16 *ptr;
+ int i;
+ __u32 sum = 0;
+ __u16 udp_length, udp_csum;
+
+ /* src ip, dst ip */
+ ptr = (__u16 *) (packet + 12);
+ for (i = 0; i < 4; ++i) {
+ sum += be16_to_cpu(ptr[i]);
+ }
+
+ /* udp protocol */
+ sum += IP_PROT_UDP;
+
+ /* udp length */
+ ptr = (__u16 *) (packet + 24);
+ udp_length = be16_to_cpu(*ptr);
+ sum += udp_length;
+
+ /* udp part */
+ ptr = (__u16 *) (packet + 20);
+ do {
+ sum += be16_to_cpu(*ptr);
+ ptr++;
+ udp_length -= 2;
+ } while (udp_length);
+
+ udp_csum = ~((__u16) ((sum & 0xffff) + (sum >> 16)));
+ return udp_csum;
+}
+
+static __u16 modify_dhcp_request(__u8 * packet, __u16 size)
+{
+ __u16 csum, new_size;
+
+ set_hw_type(packet);
+ zero_hw_len(packet);
+ zero_chaddr(packet);
+ set_bcast_flag(packet);
+ new_size = set_client_id(packet);
+ if (new_size > size) {
+ add_udp_len(packet, new_size - size);
+ } else
+ new_size = size;
+ set_udp_csum(packet, 0);
+ csum = calc_udp_csum(packet);
+ set_udp_csum(packet, csum);
+ return new_size;
+}
+
+static __u16 copy_dhcp_message(__u8 * buf, const void *packet, __u16 size)
+{
+ memcpy(buf, packet, size);
+ return size;
+}
+
+static void modify_ip_hdr(__u8 * buf, __u16 add_size)
+{
+ __u16 *ptr, ip_csum;
+ __u16 tmp;
+ __u32 sum = 0;
+ __u8 i;
+
+ /* update ip length */
+ ptr = (__u16 *) buf;
+ tmp = be16_to_cpu(ptr[1]);
+ ptr[1] = cpu_to_be16(tmp + add_size);
+
+ ptr[5] = 0; /* zero csum */
+ for (i = 0; i < 10; ++i) {
+ sum += be16_to_cpu(ptr[i]);
+ }
+
+ ip_csum = ~((__u16) ((sum & 0xffff) + (sum >> 16)));
+ ptr[5] = cpu_to_be16(ip_csum);
+
+}
+
+static void *update_dhcp_request(const void *packet, unsigned int size,
+ __u16 * new_size_p)
+{
+ __u8 ip_proto, dhcp_message_type;
+ __u16 dest_port, new_size, orig_size;
+ static __u8 dhcp_send_buffer[576];
+
+ ip_proto = get_ip_protocl_type(packet);
+ if (ip_proto != IP_PROT_UDP) {
+ return NULL;
+ }
+
+ dest_port = get_udp_dest_port(packet);
+ if (dest_port != 0x4300 /*67 */ )
+ return NULL;
+
+ dhcp_message_type = get_dhcp_message_type(packet);
+ if (dhcp_message_type != DHCP_TYPE_REQUEST)
+ return NULL;
+
+ memset(dhcp_send_buffer, 0, sizeof dhcp_send_buffer);
+ orig_size = copy_dhcp_message(dhcp_send_buffer, packet, size);
+
+ new_size = modify_dhcp_request(dhcp_send_buffer, orig_size);
+ if (new_size != orig_size) {
+ modify_ip_hdr(dhcp_send_buffer, new_size - orig_size);
+ }
+ *new_size_p = new_size;
+ return dhcp_send_buffer;
+}
+
+static int ipoib_send_packet(const __u8 * mac, __u16 protocol, const void *data,
+ unsigned int size)
+{
+ const void *packet;
+ __u16 new_size, dhcp_req_sz;
+ void *tmp;
+ int rc;
+ struct arp_packet_st ipoib_arp;
+
+ tprintf("");
+
+ if (protocol == ARP_PROT_TYPE) {
+ /* special treatment for ARP */
+ tmp = alloc_convert_arp6_msg(data, &ipoib_arp);
+ if (!tmp) {
+ eprintf("");
+ return -1;
+ }
+ packet = tmp;
+ new_size = sizeof(struct arp_packet_st);
+ tprintf("sending arp");
+ } else {
+ tmp = update_dhcp_request(data, size, &dhcp_req_sz);
+ if (tmp) {
+ /* it was a dhcp request so we use a special
+ buffer because we may have to enlarge the size of the packet */
+ tprintf("sending dhcp");
+ packet = tmp;
+ new_size = dhcp_req_sz;
+ } else {
+ packet = data;
+ new_size = size;
+ tprintf("sending packet");
+ }
+ }
+
+ //eprintf("press key ..."); getchar();
+ if (is_bcast_mac(mac)) {
+ tprintf("");
+ rc = send_bcast_packet(protocol, packet, new_size);
+ } else {
+ tprintf("");
+ rc = send_ucast_packet(mac, protocol, packet, new_size);
+ }
+
+ return rc;
+}
+
+static int ipoib_read_packet(__u16 * prot_p, void *data, unsigned int *size_p,
+ int *is_bcast_p)
+{
+ int rc;
+ struct ib_cqe_st ib_cqe;
+ __u8 num_cqes;
+ unsigned int new_size;
+ void *buf, *out_buf;
+ __u16 prot_type;
+
+ rc = ib_poll_cq(ipoib_data.rcv_cqh, &ib_cqe, &num_cqes);
+ if (rc) {
+ return rc;
+ }
+
+ if (num_cqes == 0) {
+ *size_p = 0;
+ return 0;
+ }
+
+ if (ib_cqe.is_error) {
+ eprintf("");
+ rc = -1;
+ goto ex_func;
+ }
+
+ new_size = ib_cqe.count - GRH_SIZE;
+ buf = get_rcv_wqe_buf(ib_cqe.wqe, 1);
+ tprintf("buf=%lx", buf);
+ rc = ipoib_handle_rcv(buf, &out_buf, &new_size, is_bcast_p);
+ if (rc) {
+ eprintf("");
+ rc = -1;
+ goto ex_func;
+ }
+ if (out_buf) {
+ tprintf("");
+ prot_type = get_prot_type(buf);
+ *size_p = new_size;
+ tprintf("new_size=%d", new_size);
+ if (new_size > 1560) {
+ eprintf("sizzzzzze = %d", new_size);
+ } else {
+ memcpy(data, out_buf, new_size);
+ }
+ *prot_p = prot_type;
+ } else {
+ tprintf("skip message");
+ *size_p = 0;
+ }
+
+ ex_func:
+ if (free_wqe(ib_cqe.wqe)) {
+ eprintf("");
+ }
+
+ return rc;
+}
+
+static int ipoib_init(struct pci_device *pci)
+{
+ int rc;
+ udqp_t qph;
+ int i;
+
+ tprintf("");
+ rc = ib_driver_init(pci, &qph);
+ if (rc)
+ return rc;
+
+ tprintf("");
+ ipoib_data.ipoib_qph = qph;
+ ipoib_data.ipoib_qpn = ib_get_qpn(qph);
+
+ if(print_info)
+ printf("local ipoib qpn=0x%x\n", ipoib_data.ipoib_qpn);
+
+ ipoib_data.bcast_av = ib_data.bcast_av;
+ ipoib_data.port_gid_raw = ib_data.port_gid.raw;
+ ipoib_data.snd_cqh = ib_data.ipoib_snd_cq;
+ ipoib_data.rcv_cqh = ib_data.ipoib_rcv_cq;
+
+ mac_counter = 1;
+ youth_counter = 0;
+ for (i = 0; i < NUM_MAC_ENTRIES; ++i) {
+ mac_tbl[i].valid = 0;
+ mac_tbl[i].av = NULL;
+ }
+
+ return 0;
+}
+
+static int ipoib_close(int fw_fatal)
+{
+ int rc;
+
+ rc = ib_driver_close(fw_fatal);
+
+ return rc;
+}