2005-04-16 15:20:36 -07:00
/*
* Copyright ( c ) 2004 Topspin Corporation . All rights reserved .
2005-08-10 23:03:10 -07:00
* Copyright ( c ) 2005 Sun Microsystems , Inc . All rights reserved .
2005-04-16 15:20:36 -07:00
*
* 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 from the file
* COPYING in the main directory of this source tree , or the
* OpenIB . org BSD license below :
*
* Redistribution and use in source and binary forms , with or
* without modification , are permitted provided that the following
* conditions are met :
*
* - Redistributions of source code must retain the above
* copyright notice , this list of conditions and the following
* disclaimer .
*
* - Redistributions in binary form must reproduce the above
* copyright notice , this list of conditions and the following
* disclaimer in the documentation and / or other materials
* provided with the distribution .
*
* 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 .
*/
# include <linux/errno.h>
2005-11-07 00:59:43 -08:00
# include <linux/string.h>
2011-05-27 15:29:33 -04:00
# include <linux/export.h>
2010-08-26 17:18:59 +03:00
# include <linux/if_ether.h>
2015-12-23 14:56:56 +02:00
# include <linux/ip.h>
2005-04-16 15:20:36 -07:00
2005-08-25 13:40:04 -07:00
# include <rdma/ib_pack.h>
2005-04-16 15:20:36 -07:00
# define STRUCT_FIELD(header, field) \
. struct_offset_bytes = offsetof ( struct ib_unpacked_ # # header , field ) , \
. struct_size_bytes = sizeof ( ( struct ib_unpacked_ # # header * ) 0 ) - > field , \
. field_name = # header " : " # field
static const struct ib_field lrh_table [ ] = {
{ STRUCT_FIELD ( lrh , virtual_lane ) ,
. offset_words = 0 ,
. offset_bits = 0 ,
. size_bits = 4 } ,
{ STRUCT_FIELD ( lrh , link_version ) ,
. offset_words = 0 ,
. offset_bits = 4 ,
. size_bits = 4 } ,
{ STRUCT_FIELD ( lrh , service_level ) ,
. offset_words = 0 ,
. offset_bits = 8 ,
. size_bits = 4 } ,
{ RESERVED ,
. offset_words = 0 ,
. offset_bits = 12 ,
. size_bits = 2 } ,
{ STRUCT_FIELD ( lrh , link_next_header ) ,
. offset_words = 0 ,
. offset_bits = 14 ,
. size_bits = 2 } ,
{ STRUCT_FIELD ( lrh , destination_lid ) ,
. offset_words = 0 ,
. offset_bits = 16 ,
. size_bits = 16 } ,
{ RESERVED ,
. offset_words = 1 ,
. offset_bits = 0 ,
. size_bits = 5 } ,
{ STRUCT_FIELD ( lrh , packet_length ) ,
. offset_words = 1 ,
. offset_bits = 5 ,
. size_bits = 11 } ,
{ STRUCT_FIELD ( lrh , source_lid ) ,
. offset_words = 1 ,
. offset_bits = 16 ,
. size_bits = 16 }
} ;
2010-08-26 14:17:56 +00:00
static const struct ib_field eth_table [ ] = {
{ STRUCT_FIELD ( eth , dmac_h ) ,
. offset_words = 0 ,
. offset_bits = 0 ,
. size_bits = 32 } ,
{ STRUCT_FIELD ( eth , dmac_l ) ,
. offset_words = 1 ,
. offset_bits = 0 ,
. size_bits = 16 } ,
{ STRUCT_FIELD ( eth , smac_h ) ,
. offset_words = 1 ,
. offset_bits = 16 ,
. size_bits = 16 } ,
{ STRUCT_FIELD ( eth , smac_l ) ,
. offset_words = 2 ,
. offset_bits = 0 ,
. size_bits = 32 } ,
{ STRUCT_FIELD ( eth , type ) ,
. offset_words = 3 ,
. offset_bits = 0 ,
. size_bits = 16 }
} ;
2010-08-26 17:18:59 +03:00
static const struct ib_field vlan_table [ ] = {
{ STRUCT_FIELD ( vlan , tag ) ,
. offset_words = 0 ,
. offset_bits = 0 ,
. size_bits = 16 } ,
{ STRUCT_FIELD ( vlan , type ) ,
. offset_words = 0 ,
. offset_bits = 16 ,
. size_bits = 16 }
} ;
2015-12-23 14:56:56 +02:00
static const struct ib_field ip4_table [ ] = {
{ STRUCT_FIELD ( ip4 , ver ) ,
. offset_words = 0 ,
. offset_bits = 0 ,
. size_bits = 4 } ,
{ STRUCT_FIELD ( ip4 , hdr_len ) ,
. offset_words = 0 ,
. offset_bits = 4 ,
. size_bits = 4 } ,
{ STRUCT_FIELD ( ip4 , tos ) ,
. offset_words = 0 ,
. offset_bits = 8 ,
. size_bits = 8 } ,
{ STRUCT_FIELD ( ip4 , tot_len ) ,
. offset_words = 0 ,
. offset_bits = 16 ,
. size_bits = 16 } ,
{ STRUCT_FIELD ( ip4 , id ) ,
. offset_words = 1 ,
. offset_bits = 0 ,
. size_bits = 16 } ,
{ STRUCT_FIELD ( ip4 , frag_off ) ,
. offset_words = 1 ,
. offset_bits = 16 ,
. size_bits = 16 } ,
{ STRUCT_FIELD ( ip4 , ttl ) ,
. offset_words = 2 ,
. offset_bits = 0 ,
. size_bits = 8 } ,
{ STRUCT_FIELD ( ip4 , protocol ) ,
. offset_words = 2 ,
. offset_bits = 8 ,
. size_bits = 8 } ,
{ STRUCT_FIELD ( ip4 , check ) ,
. offset_words = 2 ,
. offset_bits = 16 ,
. size_bits = 16 } ,
{ STRUCT_FIELD ( ip4 , saddr ) ,
. offset_words = 3 ,
. offset_bits = 0 ,
. size_bits = 32 } ,
{ STRUCT_FIELD ( ip4 , daddr ) ,
. offset_words = 4 ,
. offset_bits = 0 ,
. size_bits = 32 }
} ;
static const struct ib_field udp_table [ ] = {
{ STRUCT_FIELD ( udp , sport ) ,
. offset_words = 0 ,
. offset_bits = 0 ,
. size_bits = 16 } ,
{ STRUCT_FIELD ( udp , dport ) ,
. offset_words = 0 ,
. offset_bits = 16 ,
. size_bits = 16 } ,
{ STRUCT_FIELD ( udp , length ) ,
. offset_words = 1 ,
. offset_bits = 0 ,
. size_bits = 16 } ,
{ STRUCT_FIELD ( udp , csum ) ,
. offset_words = 1 ,
. offset_bits = 16 ,
. size_bits = 16 }
} ;
2005-04-16 15:20:36 -07:00
static const struct ib_field grh_table [ ] = {
{ STRUCT_FIELD ( grh , ip_version ) ,
. offset_words = 0 ,
. offset_bits = 0 ,
. size_bits = 4 } ,
{ STRUCT_FIELD ( grh , traffic_class ) ,
. offset_words = 0 ,
. offset_bits = 4 ,
. size_bits = 8 } ,
{ STRUCT_FIELD ( grh , flow_label ) ,
. offset_words = 0 ,
. offset_bits = 12 ,
. size_bits = 20 } ,
{ STRUCT_FIELD ( grh , payload_length ) ,
. offset_words = 1 ,
. offset_bits = 0 ,
. size_bits = 16 } ,
{ STRUCT_FIELD ( grh , next_header ) ,
. offset_words = 1 ,
. offset_bits = 16 ,
. size_bits = 8 } ,
{ STRUCT_FIELD ( grh , hop_limit ) ,
. offset_words = 1 ,
. offset_bits = 24 ,
. size_bits = 8 } ,
{ STRUCT_FIELD ( grh , source_gid ) ,
. offset_words = 2 ,
. offset_bits = 0 ,
. size_bits = 128 } ,
{ STRUCT_FIELD ( grh , destination_gid ) ,
. offset_words = 6 ,
. offset_bits = 0 ,
. size_bits = 128 }
} ;
static const struct ib_field bth_table [ ] = {
{ STRUCT_FIELD ( bth , opcode ) ,
. offset_words = 0 ,
. offset_bits = 0 ,
. size_bits = 8 } ,
{ STRUCT_FIELD ( bth , solicited_event ) ,
. offset_words = 0 ,
. offset_bits = 8 ,
. size_bits = 1 } ,
{ STRUCT_FIELD ( bth , mig_req ) ,
. offset_words = 0 ,
. offset_bits = 9 ,
. size_bits = 1 } ,
{ STRUCT_FIELD ( bth , pad_count ) ,
. offset_words = 0 ,
. offset_bits = 10 ,
. size_bits = 2 } ,
{ STRUCT_FIELD ( bth , transport_header_version ) ,
. offset_words = 0 ,
. offset_bits = 12 ,
. size_bits = 4 } ,
{ STRUCT_FIELD ( bth , pkey ) ,
. offset_words = 0 ,
. offset_bits = 16 ,
. size_bits = 16 } ,
{ RESERVED ,
. offset_words = 1 ,
. offset_bits = 0 ,
. size_bits = 8 } ,
{ STRUCT_FIELD ( bth , destination_qpn ) ,
. offset_words = 1 ,
. offset_bits = 8 ,
. size_bits = 24 } ,
{ STRUCT_FIELD ( bth , ack_req ) ,
. offset_words = 2 ,
. offset_bits = 0 ,
. size_bits = 1 } ,
{ RESERVED ,
. offset_words = 2 ,
. offset_bits = 1 ,
. size_bits = 7 } ,
{ STRUCT_FIELD ( bth , psn ) ,
. offset_words = 2 ,
. offset_bits = 8 ,
. size_bits = 24 }
} ;
static const struct ib_field deth_table [ ] = {
{ STRUCT_FIELD ( deth , qkey ) ,
. offset_words = 0 ,
. offset_bits = 0 ,
. size_bits = 32 } ,
{ RESERVED ,
. offset_words = 1 ,
. offset_bits = 0 ,
. size_bits = 8 } ,
{ STRUCT_FIELD ( deth , source_qpn ) ,
. offset_words = 1 ,
. offset_bits = 8 ,
. size_bits = 24 }
} ;
2016-01-14 17:50:41 +02:00
__sum16 ib_ud_ip4_csum ( struct ib_ud_header * header )
2015-12-23 14:56:56 +02:00
{
struct iphdr iph ;
iph . ihl = 5 ;
iph . version = 4 ;
iph . tos = header - > ip4 . tos ;
iph . tot_len = header - > ip4 . tot_len ;
iph . id = header - > ip4 . id ;
iph . frag_off = header - > ip4 . frag_off ;
iph . ttl = header - > ip4 . ttl ;
iph . protocol = header - > ip4 . protocol ;
iph . check = 0 ;
iph . saddr = header - > ip4 . saddr ;
iph . daddr = header - > ip4 . daddr ;
return ip_fast_csum ( ( u8 * ) & iph , iph . ihl ) ;
}
EXPORT_SYMBOL ( ib_ud_ip4_csum ) ;
2005-04-16 15:20:36 -07:00
/**
* ib_ud_header_init - Initialize UD header structure
* @ payload_bytes : Length of packet payload
2010-08-26 14:17:56 +00:00
* @ lrh_present : specify if LRH is present
* @ eth_present : specify if Eth header is present
2010-08-26 17:18:59 +03:00
* @ vlan_present : packet is tagged vlan
2015-12-23 14:56:56 +02:00
* @ grh_present : GRH flag ( if non - zero , GRH will be included )
* @ ip_version : if non - zero , IP header , V4 or V6 , will be included
* @ udp_present : if non - zero , UDP header will be included
2010-08-26 14:17:56 +00:00
* @ immediate_present : specify if immediate data is present
2005-04-16 15:20:36 -07:00
* @ header : Structure to initialize
*/
2015-12-23 14:56:56 +02:00
int ib_ud_header_init ( int payload_bytes ,
int lrh_present ,
int eth_present ,
int vlan_present ,
int grh_present ,
int ip_version ,
int udp_present ,
int immediate_present ,
struct ib_ud_header * header )
2005-04-16 15:20:36 -07:00
{
2016-01-28 17:51:50 +02:00
size_t udp_bytes = udp_present ? IB_UDP_BYTES : 0 ;
2015-12-23 14:56:56 +02:00
grh_present = grh_present & & ! ip_version ;
2005-04-16 15:20:36 -07:00
memset ( header , 0 , sizeof * header ) ;
2015-12-23 14:56:56 +02:00
/*
* UDP header without IP header doesn ' t make sense
*/
if ( udp_present & & ip_version ! = 4 & & ip_version ! = 6 )
return - EINVAL ;
2010-08-26 14:17:56 +00:00
if ( lrh_present ) {
u16 packet_length ;
header - > lrh . link_version = 0 ;
header - > lrh . link_next_header =
grh_present ? IB_LNH_IBA_GLOBAL : IB_LNH_IBA_LOCAL ;
packet_length = ( IB_LRH_BYTES +
IB_BTH_BYTES +
IB_DETH_BYTES +
( grh_present ? IB_GRH_BYTES : 0 ) +
payload_bytes +
4 + /* ICRC */
3 ) / 4 ; /* round up */
header - > lrh . packet_length = cpu_to_be16 ( packet_length ) ;
}
2005-04-16 15:20:36 -07:00
2010-08-26 17:18:59 +03:00
if ( vlan_present )
header - > eth . type = cpu_to_be16 ( ETH_P_8021Q ) ;
2015-12-23 14:56:56 +02:00
if ( ip_version = = 6 | | grh_present ) {
2005-04-16 15:20:36 -07:00
header - > grh . ip_version = 6 ;
header - > grh . payload_length =
2016-01-28 17:51:50 +02:00
cpu_to_be16 ( ( udp_bytes +
IB_BTH_BYTES +
2005-04-16 15:20:36 -07:00
IB_DETH_BYTES +
payload_bytes +
4 + /* ICRC */
3 ) & ~ 3 ) ; /* round up */
2015-12-23 14:56:56 +02:00
header - > grh . next_header = udp_present ? IPPROTO_UDP : 0x1b ;
2005-04-16 15:20:36 -07:00
}
2015-12-23 14:56:56 +02:00
if ( ip_version = = 4 ) {
header - > ip4 . ver = 4 ; /* version 4 */
header - > ip4 . hdr_len = 5 ; /* 5 words */
header - > ip4 . tot_len =
cpu_to_be16 ( IB_IP4_BYTES +
udp_bytes +
IB_BTH_BYTES +
IB_DETH_BYTES +
payload_bytes +
4 ) ; /* ICRC */
header - > ip4 . protocol = IPPROTO_UDP ;
}
if ( udp_present & & ip_version )
header - > udp . length =
cpu_to_be16 ( IB_UDP_BYTES +
IB_BTH_BYTES +
IB_DETH_BYTES +
payload_bytes +
4 ) ; /* ICRC */
2010-02-08 11:40:37 +00:00
if ( immediate_present )
2005-04-16 15:20:36 -07:00
header - > bth . opcode = IB_OPCODE_UD_SEND_ONLY_WITH_IMMEDIATE ;
else
header - > bth . opcode = IB_OPCODE_UD_SEND_ONLY ;
header - > bth . pad_count = ( 4 - payload_bytes ) & 3 ;
header - > bth . transport_header_version = 0 ;
2010-08-26 14:17:56 +00:00
header - > lrh_present = lrh_present ;
header - > eth_present = eth_present ;
2010-08-26 17:18:59 +03:00
header - > vlan_present = vlan_present ;
2015-12-23 14:56:56 +02:00
header - > grh_present = grh_present | | ( ip_version = = 6 ) ;
header - > ipv4_present = ip_version = = 4 ;
header - > udp_present = udp_present ;
2010-08-26 14:17:56 +00:00
header - > immediate_present = immediate_present ;
2015-12-23 14:56:56 +02:00
return 0 ;
2005-04-16 15:20:36 -07:00
}
EXPORT_SYMBOL ( ib_ud_header_init ) ;
/**
* ib_ud_header_pack - Pack UD header struct into wire format
* @ header : UD header struct
* @ buf : Buffer to pack into
*
* ib_ud_header_pack ( ) packs the UD header structure @ header into wire
* format in the buffer @ buf .
*/
int ib_ud_header_pack ( struct ib_ud_header * header ,
void * buf )
{
int len = 0 ;
2010-08-26 14:17:56 +00:00
if ( header - > lrh_present ) {
ib_pack ( lrh_table , ARRAY_SIZE ( lrh_table ) ,
& header - > lrh , buf + len ) ;
len + = IB_LRH_BYTES ;
}
if ( header - > eth_present ) {
ib_pack ( eth_table , ARRAY_SIZE ( eth_table ) ,
& header - > eth , buf + len ) ;
len + = IB_ETH_BYTES ;
}
2010-08-26 17:18:59 +03:00
if ( header - > vlan_present ) {
ib_pack ( vlan_table , ARRAY_SIZE ( vlan_table ) ,
& header - > vlan , buf + len ) ;
len + = IB_VLAN_BYTES ;
}
2005-04-16 15:20:36 -07:00
if ( header - > grh_present ) {
ib_pack ( grh_table , ARRAY_SIZE ( grh_table ) ,
& header - > grh , buf + len ) ;
len + = IB_GRH_BYTES ;
}
2015-12-23 14:56:56 +02:00
if ( header - > ipv4_present ) {
ib_pack ( ip4_table , ARRAY_SIZE ( ip4_table ) ,
& header - > ip4 , buf + len ) ;
len + = IB_IP4_BYTES ;
}
if ( header - > udp_present ) {
ib_pack ( udp_table , ARRAY_SIZE ( udp_table ) ,
& header - > udp , buf + len ) ;
len + = IB_UDP_BYTES ;
}
2005-04-16 15:20:36 -07:00
ib_pack ( bth_table , ARRAY_SIZE ( bth_table ) ,
& header - > bth , buf + len ) ;
len + = IB_BTH_BYTES ;
ib_pack ( deth_table , ARRAY_SIZE ( deth_table ) ,
& header - > deth , buf + len ) ;
len + = IB_DETH_BYTES ;
if ( header - > immediate_present ) {
memcpy ( buf + len , & header - > immediate_data , sizeof header - > immediate_data ) ;
len + = sizeof header - > immediate_data ;
}
return len ;
}
EXPORT_SYMBOL ( ib_ud_header_pack ) ;
/**
* ib_ud_header_unpack - Unpack UD header struct from wire format
* @ header : UD header struct
* @ buf : Buffer to pack into
*
* ib_ud_header_pack ( ) unpacks the UD header structure @ header from wire
* format in the buffer @ buf .
*/
int ib_ud_header_unpack ( void * buf ,
struct ib_ud_header * header )
{
ib_unpack ( lrh_table , ARRAY_SIZE ( lrh_table ) ,
buf , & header - > lrh ) ;
buf + = IB_LRH_BYTES ;
if ( header - > lrh . link_version ! = 0 ) {
2016-03-02 00:50:29 +05:30
pr_warn ( " Invalid LRH.link_version %d \n " ,
header - > lrh . link_version ) ;
2005-04-16 15:20:36 -07:00
return - EINVAL ;
}
switch ( header - > lrh . link_next_header ) {
case IB_LNH_IBA_LOCAL :
header - > grh_present = 0 ;
break ;
case IB_LNH_IBA_GLOBAL :
header - > grh_present = 1 ;
ib_unpack ( grh_table , ARRAY_SIZE ( grh_table ) ,
buf , & header - > grh ) ;
buf + = IB_GRH_BYTES ;
if ( header - > grh . ip_version ! = 6 ) {
2016-03-02 00:50:29 +05:30
pr_warn ( " Invalid GRH.ip_version %d \n " ,
header - > grh . ip_version ) ;
2005-04-16 15:20:36 -07:00
return - EINVAL ;
}
if ( header - > grh . next_header ! = 0x1b ) {
2016-03-02 00:50:29 +05:30
pr_warn ( " Invalid GRH.next_header 0x%02x \n " ,
header - > grh . next_header ) ;
2005-04-16 15:20:36 -07:00
return - EINVAL ;
}
break ;
default :
2016-03-02 00:50:29 +05:30
pr_warn ( " Invalid LRH.link_next_header %d \n " ,
header - > lrh . link_next_header ) ;
2005-04-16 15:20:36 -07:00
return - EINVAL ;
}
ib_unpack ( bth_table , ARRAY_SIZE ( bth_table ) ,
buf , & header - > bth ) ;
buf + = IB_BTH_BYTES ;
switch ( header - > bth . opcode ) {
case IB_OPCODE_UD_SEND_ONLY :
header - > immediate_present = 0 ;
break ;
case IB_OPCODE_UD_SEND_ONLY_WITH_IMMEDIATE :
header - > immediate_present = 1 ;
break ;
default :
2016-03-02 00:50:29 +05:30
pr_warn ( " Invalid BTH.opcode 0x%02x \n " , header - > bth . opcode ) ;
2005-04-16 15:20:36 -07:00
return - EINVAL ;
}
if ( header - > bth . transport_header_version ! = 0 ) {
2016-03-02 00:50:29 +05:30
pr_warn ( " Invalid BTH.transport_header_version %d \n " ,
header - > bth . transport_header_version ) ;
2005-04-16 15:20:36 -07:00
return - EINVAL ;
}
ib_unpack ( deth_table , ARRAY_SIZE ( deth_table ) ,
buf , & header - > deth ) ;
buf + = IB_DETH_BYTES ;
if ( header - > immediate_present )
memcpy ( & header - > immediate_data , buf , sizeof header - > immediate_data ) ;
return 0 ;
}
EXPORT_SYMBOL ( ib_ud_header_unpack ) ;