How do I access header and data information within a mBlk ethernet package? specifically I want to extract a sender IP - network-programming

So far my understanding of achieveing this is using NET_FUNCS:
typedef struct net_funcs{
STATUS (*start) (void*); /* driver’s start func */
STATUS (*stop) (void*); /* driver’s stop func */
STATUS (*unload) (void*); /* driver’s unload func */
int (*ioctl) (void*, int, caddr_t); /* driver’s ioctl func */
STATUS (*send) (void* , M_BLK_ID); /* driver’s send func */
STATUS (*mCastAddrAdd) (void*, char*); /* driver’s mcast add func */
STATUS (*mCastAddrDel) (void*, char*); /* driver’s mcast delete func */
STATUS (*mCastAddrGet) (void*, MULTI_TABLE*);/* driver’s mcast get func */
STATUS (*pollSend) (void*, M_BLK_ID); /* driver’s poll send func */
STATUS (*pollRcv) (void*, M_BLK_ID); /* driver’s poll receive func */
STATUS (*addressForm) (M_BLK_ID, M_BLK_ID, M_BLK_ID);/* driver’s addr formation func */
STATUS (*packetDataGet) (M_BLK_ID, M_BLK_ID);/* driver’s pkt data get func */
STATUS (*addrGet) (M_BLK_ID, M_BLK_ID, M_BLK_ID, M_BLK_ID, M_BLK_ID);/* driver’s pkt addr get func */} NET_FUNCS;
I have a pointer to a M_BLK struct called pMblk(of type M_BLK_ID = * M_BLK) that I believe holds the information to our Ethernet package(header and data), this is where my understanding starts to fade, I'm not 100% sure this is where our whole package is stored or is just part of a whole(cluster). Specifically using adderGet (using a same function but for our case its endEtherPacketAddrGet), The way I understand this function is that if you put your structure with all the information from the packet into the param 1, and followed with other pointers I created param 2: pSrc, param 3: pDest, param 4: pESrc, param 5: pEDst, I seems like it would make sense that the information for the senders IP address would be somewhere withing the pSrc Struct. This is where I've really struggled in printing out the information with in this object as a log message, For now I just want to print out the sender IP address, later more info but I havent been able to print anything coherent.
I've tried:
printf("pSrc->mData: %d", pSrc->mData); // not actually using print just for the sake of the example
printf("pSrc->mData: %c", pSrc->mData); // not actually using print just for the sake of the example
yielded blank or not useful data, also:
printf("pSrc->mData: %d", &pSrc->mData); // not actually using print just for the sake of the example
printf("pSrc->mData: %c", &pSrc->mData); // not actually using print just for the sake of the example
These are not the only things I've tried I just want to give a feeling of where I am and what I am trying to do, that being said is there a fundamental understanding that I'm off on here because I feel as though I'm close? Is there a different approach to reaching this information, or is it stored in a different member of the structure? Any guidance on this would be greatly appreciated!

These c code pieces would be helpful.
unsigned char *mData = (unsigned char *)(pMblk->mBlkHdr.mData);
IP_HEADER_T *pkt_iph = (IP_HEADER_T *)&mData[14]; /* ethernet packet only */
unsigned int src_ip_addr = pkt_iph->src;
unsigned int dst_ip_addr = pkt_iph->dst;

solved this using pointer arithmetic:
srcAddr[0] = *(mBlk->m_data + 6);
srcAddr[1] = *(mBlk->m_data + 7);
srcAddr[2] = *(mBlk->m_data + 8);
srcAddr[3] = *(mBlk->m_data + 9);
srcAddr[4] = *(mBlk->m_data + 10);
srcAddr[5] = *(mBlk->m_data + 11);
printf("Source Address: %02x:%02x:%02x:%02x:%02x:%02x\n", srcAddr[0], srcAddr[1], srcAddr[2], srcAddr[3], srcAddr[4], srcAddr[5]);
m_data is a typedef for mBlk->mBlkHdr.mData

Little late to the party, but to specifically arrange the data pointers to the exact location within the message block:
unsigned char *destAddr;
unsigned char *srcAddr;
LL_HDR_INFO *hdrInfo;
/*Pass in the packet and the LL_HDR_INFO object*/
endEtherPacketDataGet(mBlk, hdrInfo);
/*Copy from source to destination the number of bytes passed in*/
bcopy(mBlk->mBlkHdr.mData + hdrInfo->destAddrOffset, destAddr, hdrInfo->destSize);
/*Print the 6 byte physical address*/
printf("Destination Address: %02x:%02x:%02x:%02x:%02x:%02x\n", destAddr[0], destAddr[1], destAddr[2], destAddr[3], destAddr[4], destAddr[5]);
bcopy(mBlk->mBlkHdr.mData + hdrInfo->srcAddrOffset, srcAddr, hdrInfo->srcSize);
printf("Source Address: %02x:%02x:%02x:%02x:%02x:%02x\n", srcAddr[0], srcAddr[1], srcAddr[2], srcAddr[3], srcAddr[4], srcAddr[5]);
This will allow you to debug the information within the received packet.

Related

why use cpu_to_le32 for the DMA address but not for length? (in an example code in a book)

I was reading the book Essential Linux Device Driver (by Sreekrishnan Venkateswaran) and in Chapter 10 Listing 10.5. Setting Up DMA Descriptors and Buffers, I see
/* Device-specific data structure for the Ethernet Function */
struct device_data {
struct pci_dev *pdev; /* The PCI Device structure */
struct net_device *ndev; /* The Net Device structure */
void *dma_buffer_rx; /* Kernel virtual address of the receive descriptor */
dma_addr_t dma_bus_rx; /* Bus address of the receive descriptor */
void *dma_buffer_tx; /* Kernel virtual address of the transmit descriptor */
dma_addr_t dma_bus_tx; /* Bus address of the transmit descriptor */
/* ... */
spin_lock_t device_lock; /* Serialize */
} *mydev_data;
/* On-card registers related to DMA */
#define DMA_RX_REGISTER_OFFSET 0x0 /* Offset of the register holding the bus address of the RX descriptor */
#define DMA_TX_REGISTER_OFFSET 0x4 /* Offset of the register holding the bus address of the TX descriptor */
#define CONTROL_REGISTER 0x8 /* Offset of the control register */
/* Control Register Defines */
#define INITIATE_XMIT 0x1
/* Descriptor control word definitions */
#define FREE_FLAG 0x1 /* Free Descriptor */
#define INTERRUPT_FLAG 0x2 /* Assert interrupt after DMA */
/* Invoked from Listing 10.3 */
static void dma_descriptor_setup(struct pci_dev *pdev)
{
/* Allocate receive DMA descriptors and buffers */
mydev_data->dma_buffer_rx = pci_alloc_consistent(pdev, 3096, &mydev_data->dma_bus_rx);
/* Fill the two receive descriptors as shown in Figure 10.2 */
/* RX descriptor 1 */
mydev_data->dma_buffer_rx[0] = cpu_to_le32((unsigned long)(mydev_data->dma_bus_rx + 24)); /* Buffer address */
mydev_data->dma_buffer_rx[1] = 1536; /* Buffer length */
mydev_data->dma_buffer_rx[2] = FREE_FLAG; /* Descriptor is free */
/* RX descriptor 2 */
mydev_data->dma_buffer_rx[3] = cpu_to_le32((unsigned long)(mydev_data->dma_bus_rx + 1560)); /* Buffer address */
mydev_data->dma_buffer_rx[4] = 1536; /* Buffer length */
mydev_data->dma_buffer_rx[5] = FREE_FLAG; /* Descriptor is free */
wmb(); /* Write Memory Barrier */
/* Write the address of the receive descriptor to the appropriate register in the card. The I/O base address, ioaddr, was populated in Listing 10.3 */
outl(cpu_to_le32((unsigned long)mydev_data->dma_bus_rx), ioaddr + DMA_RX_REGISTER_OFFSET);
/* Allocate transmit DMA descriptors and buffers */
mydev_data->dma_buffer_tx = pci_alloc_consistent(pdev, 3096, &mydev_data->dma_bus_tx);
/* Fill the two transmit descriptors as shown in Figure 10.2 */
/* TX descriptor 1 */
mydev_data->dma_buffer_tx[0] = cpu_to_le32((unsigned long)(mydev_data->dma_bus_tx + 24)); /* Buffer address */ <---- line A
mydev_data->dma_buffer_tx[1] = 1536; /* Buffer length */ <---- line B
/* Valid descriptor. Generate an interrupt after completing the DMA */
mydev_data->dma_buffer_tx[2] = (FREE_FLAG | INTERRUPT_FLAG);
/* TX descriptor 2 */
mydev_data->dma_buffer_tx[3] = cpu_to_le32((unsigned long)(mydev_data->dma_bus_tx + 1560)); /* Buffer address */
mydev_data->dma_buffer_tx[4] = 1536; /* Buffer length */
mydev_data->dma_buffer_tx[5] = (FREE_FLAG | INTERRUPT_FLAG);
wmb(); /* Write Memory Barrier */
/* Write the address of the transmit descriptor to the appropriate register in the card. The I/O base, ioaddr, was populated in Listing 10.3 */
outl(cpu_to_le32((unsigned long)mydev_data->dma_bus_tx), ioaddr + DMA_TX_REGISTER_OFFSET);
}
/* Invoked from Listing 10.3 */
static void dma_descriptor_release(struct pci_dev *pdev)
{
pci_free_consistent(pdev, 3096, mydev_data->dma_bus_tx);
pci_free_consistent(pdev, 3096, mydev_data->dma_bus_rx);
}
In the code, the driver prepares a buffer for the DMA descriptors and DMA buffers using pci_alloc_consistent() and sets them up and passes the buffer address (bus address) to the hardware making sure it's in little endian format using cpu_to_le32(). So I understood the H/W sees the buffer descriptor. But in the descriptor, why did it use cpu_to_le32() for the descriptor address (line A above) and not for the following buffer length (line B above)? Does the H/W see only the buffer address and not the size? Or is it an error in the book? By the way, this is for a fictitious PCI ethernet chip driver.
In practice such approach indeed looks like a mistake. But theoretically it's possible that one field (address) is always little endian no matter what, while another (length) is in native mode. Nowadays, with help of FPGA, I guess one even may try to implement this theoretical case.
So, in the given context, especially taking into consideration your remark that this is for a fictitious PCI ethernet chip, it is a bit hard to say what was author's intention here.

how to use collect-view in Cooja in my own application

i have a question about collect-view. Can collect-view be used in other applications? i simulate some applications by cooja, and want to collect information about network,such as network map,ETX and so on. how can i use collect-view to collect information about my own applications?
thanks!
In the apps/collect-view or in your application you can add your own information to be sent by collect by redefining collect_view_arch_read_sensors, check for example how it is done for the Z1 mote in the collect-view-z1.c:
#include "collect-view.h"
#include "cc2420.h"
#include "dev/leds.h"
#include "dev/i2cmaster.h"
#include "dev/tmp102.h"
#include "collect-view-z1.h"
/*---------------------------------------------------------------------------*/
static uint16_t
get_temp()
{
/* XXX Fix me: check /examples/z1/test-tmp102.c for correct conversion */
return (uint16_t)tmp102_read_temp_raw();
}
/*---------------------------------------------------------------------------*/
void
collect_view_arch_read_sensors(struct collect_view_data_msg *msg)
{
static int initialized = 0;
if(!initialized) {
tmp102_init();
initialized = 1;
}
msg->sensors[BATTERY_VOLTAGE_SENSOR] = 0;
msg->sensors[BATTERY_INDICATOR] = 0;
msg->sensors[LIGHT1_SENSOR] = 0;
msg->sensors[LIGHT2_SENSOR] = 0;
msg->sensors[TEMP_SENSOR] = get_temp();
msg->sensors[HUMIDITY_SENSOR] = 0;
}
Notice that collect_view_arch_read_sensors(msg); is called by collect-view.c when you call collect_view_construct_message(...) from your application. This is how it is done in the examples/ipv6/rpl-collect/udp-sender.c example:
/* Collect structure */
struct {
uint8_t seqno;
uint8_t for_alignment;
struct collect_view_data_msg msg;
} msg;
/* Create the collect structure */
collect_view_construct_message(&msg.msg, &parent,
parent_etx, rtmetric,
num_neighbors, beacon_interval);
/* Send over UDP */
uip_udp_packet_sendto(client_conn, &msg, sizeof(msg),
&server_ipaddr, UIP_HTONS(UDP_SERVER_PORT));
When contiki calls the Call back function, for example the recv() function in rime-protocol, The sink node should use printf() to print out the sensor data. This output then will be read by Collect View GUI per-line.
take a look at
tools/collect-view/src/org/contikios/contiki/collect/CollectServer.java
The handleIncomingData() called by serialData(), then checks if it's a sensor data or not in SensorData.parseSensorData().
It will ignore if empty, incorrect format, comment, or annotation
sensor data will be displayed in Collect View GUI if meets some condition. for example, the data length calculated match with datalen implisit on its sensor data.

Network Activity Monitoring on iPhone

I've been working for 5 days trying to learn and implement Network monitor on the iPhone.
I looked into netstat code from apple, and i lost like 25% of my hair.
I found links for JB Devices but i need it to execute on a non JB device. (Irrespective of whether Apple accepts it on the App store or not).
I found some useful links :
how to get tcp udp opening port list on iPhone (I couldn't parse the data returned in this question :( )
Data Usage on iPhone
sysctlbyname buf return type (I'm not a networking guy..could not understand this one, may be you guys can help :) )
TCP/UPD port list
I can say i got some thing from the first link. Can you guys help me to Parse the data ?
Is there any other method to achieve this??
Ok, you have all that you need over the table.
You can check the question Is there any private api to monitor network traffic on iPhone?
And here you can find the source code of inet. That code have all you need to parse the data returned of how to get tcp udp opening port list on iPhone
size_t len = 0;
if (sysctlbyname("net.inet.tcp.pcblist_n", 0, &len, 0, 0) < 0) {
perror("sysctlbyname");
} else {
char *buf = malloc(len);
sysctlbyname("net.inet.tcp.pcblist_n", buf, &len, 0, 0);
NSData *data = [NSData dataWithBytesNoCopy:buf length:len];
NSLog(#"data = %#", data);
}
Ok, yes the source code of inet is a little complicate. But, you know that netstat print the net status. So, you only need to see when inet execute printf, at this point you will have the data parsed.
If you try to compile the source of inet for iOS, you will find a lot of problems: some header files are not present in ios sdk. Ok, no problem copy the headers.
For the simulator you only need copy the header of netstat.h. And add some struct declaration that are private:
struct xtcpcb_n {
u_int32_t xt_len;
u_int32_t xt_kind; /* XSO_TCPCB */
u_int64_t t_segq;
int t_dupacks; /* consecutive dup acks recd */
int t_timer[TCPT_NTIMERS_EXT]; /* tcp timers */
int t_state; /* state of this connection */
u_int t_flags;
int t_force; /* 1 if forcing out a byte */
tcp_seq snd_una; /* send unacknowledged */
tcp_seq snd_max; /* highest sequence number sent;
* used to recognize retransmits
*/
tcp_seq snd_nxt; /* send next */
tcp_seq snd_up; /* send urgent pointer */
tcp_seq snd_wl1; /* window update seg seq number */
tcp_seq snd_wl2; /* window update seg ack number */
tcp_seq iss; /* initial send sequence number */
tcp_seq irs; /* initial receive sequence number */
tcp_seq rcv_nxt; /* receive next */
tcp_seq rcv_adv; /* advertised window */
u_int32_t rcv_wnd; /* receive window */
tcp_seq rcv_up; /* receive urgent pointer */
u_int32_t snd_wnd; /* send window */
u_int32_t snd_cwnd; /* congestion-controlled window */
u_int32_t snd_ssthresh; /* snd_cwnd size threshold for
* for slow start exponential to
* linear switch
*/
u_int t_maxopd; /* mss plus options */
u_int32_t t_rcvtime; /* time at which a packet was received */
u_int32_t t_starttime; /* time connection was established */
int t_rtttime; /* round trip time */
tcp_seq t_rtseq; /* sequence number being timed */
int t_rxtcur; /* current retransmit value (ticks) */
u_int t_maxseg; /* maximum segment size */
int t_srtt; /* smoothed round-trip time */
int t_rttvar; /* variance in round-trip time */
int t_rxtshift; /* log(2) of rexmt exp. backoff */
u_int t_rttmin; /* minimum rtt allowed */
u_int32_t t_rttupdated; /* number of times rtt sampled */
u_int32_t max_sndwnd; /* largest window peer has offered */
int t_softerror; /* possible error not yet reported */
/* out-of-band data */
char t_oobflags; /* have some */
char t_iobc; /* input character */
/* RFC 1323 variables */
u_char snd_scale; /* window scaling for send window */
u_char rcv_scale; /* window scaling for recv window */
u_char request_r_scale; /* pending window scaling */
u_char requested_s_scale;
u_int32_t ts_recent; /* timestamp echo data */
u_int32_t ts_recent_age; /* when last updated */
tcp_seq last_ack_sent;
/* RFC 1644 variables */
tcp_cc cc_send; /* send connection count */
tcp_cc cc_recv; /* receive connection count */
tcp_seq snd_recover; /* for use in fast recovery */
/* experimental */
u_int32_t snd_cwnd_prev; /* cwnd prior to retransmit */
u_int32_t snd_ssthresh_prev; /* ssthresh prior to retransmit */
u_int32_t t_badrxtwin; /* window for retransmit recovery */
};
struct xinpcb_n {
u_int32_t xi_len; /* length of this structure */
u_int32_t xi_kind; /* XSO_INPCB */
u_int64_t xi_inpp;
u_short inp_fport; /* foreign port */
u_short inp_lport; /* local port */
u_int64_t inp_ppcb; /* pointer to per-protocol pcb */
inp_gen_t inp_gencnt; /* generation count of this instance */
int inp_flags; /* generic IP/datagram flags */
u_int32_t inp_flow;
u_char inp_vflag;
u_char inp_ip_ttl; /* time to live */
u_char inp_ip_p; /* protocol */
union { /* foreign host table entry */
struct in_addr_4in6 inp46_foreign;
struct in6_addr inp6_foreign;
} inp_dependfaddr;
union { /* local host table entry */
struct in_addr_4in6 inp46_local;
struct in6_addr inp6_local;
} inp_dependladdr;
struct {
u_char inp4_ip_tos; /* type of service */
} inp_depend4;
struct {
u_int8_t inp6_hlim;
int inp6_cksum;
u_short inp6_ifindex;
short inp6_hops;
} inp_depend6;
u_int32_t inp_flowhash;
};
#define SO_TC_STATS_MAX 4
struct data_stats {
u_int64_t rxpackets;
u_int64_t rxbytes;
u_int64_t txpackets;
u_int64_t txbytes;
};
struct xgen_n {
u_int32_t xgn_len; /* length of this structure */
u_int32_t xgn_kind; /* number of PCBs at this time */
};
#define XSO_SOCKET 0x001
#define XSO_RCVBUF 0x002
#define XSO_SNDBUF 0x004
#define XSO_STATS 0x008
#define XSO_INPCB 0x010
#define XSO_TCPCB 0x020
struct xsocket_n {
u_int32_t xso_len; /* length of this structure */
u_int32_t xso_kind; /* XSO_SOCKET */
u_int64_t xso_so; /* makes a convenient handle */
short so_type;
u_int32_t so_options;
short so_linger;
short so_state;
u_int64_t so_pcb; /* another convenient handle */
int xso_protocol;
int xso_family;
short so_qlen;
short so_incqlen;
short so_qlimit;
short so_timeo;
u_short so_error;
pid_t so_pgid;
u_int32_t so_oobmark;
uid_t so_uid; /* XXX */
};
struct xsockbuf_n {
u_int32_t xsb_len; /* length of this structure */
u_int32_t xsb_kind; /* XSO_RCVBUF or XSO_SNDBUF */
u_int32_t sb_cc;
u_int32_t sb_hiwat;
u_int32_t sb_mbcnt;
u_int32_t sb_mbmax;
int32_t sb_lowat;
short sb_flags;
short sb_timeo;
};
struct xsockstat_n {
u_int32_t xst_len; /* length of this structure */
u_int32_t xst_kind; /* XSO_STATS */
struct data_stats xst_tc_stats[SO_TC_STATS_MAX];
};
But for the Device you need to copy some other headers of the kernel, I don't know why the headers aren't in the device sdk (maybe if you use them Apple will not approve your app, I don't know, but that doesn't matter).
You can copy the missing headers from the simulator SDK to your project. Or you can change the header path to the simulator SDK. (I copied the headers).
If you copy the headers you will need change some include declaration.
You don't need all the inet.c code. You only need these functions:
protopr
inetprint
inetname
Then inside those functions you will see the printf with the data parsed, you can add that in a dictionary.
Here is my code. You can see a class named: DHInet with two method that return a NSArray with a list of NSDictionary with the information of the connections.
I hope you find it useful, and if anyone wants to give me a hand in improving the code, so be it, I need to clean the code, because it has a lot of ifdef that are not necessary.

Cannot upload data get xivelyclient.put returned -1

Background:
I am trying to upload data from a simple on off sensor to learn the Xively methods. I am using an Arduino Uno with a WiFi Shield. I made some simple alterations to an example sketch from the Xively library to keep it very simple. I have read the documentation and the FAQ's plus searched the web. There was a similar question (Can't connect to Xively using Arduino + wifi shield, "ret = -1 No sockets available) and the answer was to reduce the number of libraries loaded. I'm using the same library list recommended in that post.I have also updated my Arduino IDE and downloaded the latest xively and HTTP library. The code compiles without error. I re-loaded my device on the xively website and got a new API key and number as well. Plus, I ensured the channel was added with the correct sensor name. I have also checked my router and ensured the WiFi shield was connecting properly.
Problem: I can't see the data on the Xively website and I keep getting the following error messages on the serial monitor from the Arduino:
xivelyclint.put returned -1
also, after several tries, I get "No socket available"
Question: Is there an problem with the code or is it something else?
Current Arduino code (actual ssid, password and API key and number removed):
#include <SPI.h>
#include <WiFi.h>
#include <HttpClient.h>
#include <Xively.h>
char ssid[] = "ssid"; // your network SSID (name)
char pass[] = "password"; // your network password (use for WPA, or use as key for WEP)
int keyIndex = 0; // your network key Index number (needed only for WEP)
int status = WL_IDLE_STATUS;
// Your Xively key to let you upload data
char xivelyKey[] = "api_key";
// Analog pin which we're monitoring (0 and 1 are used by the Ethernet shield)
int sensorPin = 2;
// Define the strings for our datastream IDs
char sensorId[] = "sensor_id";
XivelyDatastream datastreams[] = {
XivelyDatastream(sensorId, strlen(sensorId), DATASTREAM_INT),
};
// Finally, wrap the datastreams into a feed
XivelyFeed feed(feed no., datastreams, 1 /* number of datastreams */);
WiFiClient client;
XivelyClient xivelyclient(client);
void printWifiStatus() {
// print the SSID of the network you're attached to:
Serial.print("SSID: ");
Serial.println(WiFi.SSID());
// print your WiFi shield's IP address:
IPAddress ip = WiFi.localIP();
Serial.print("IP Address: ");
Serial.println(ip);
// print the received signal strength:
long rssi = WiFi.RSSI();
Serial.print("signal strength (RSSI):");
Serial.print(rssi);
Serial.println(" dBm");
}
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
Serial.println("Starting single datastream upload to Xively...");
Serial.println();
// attempt to connect to Wifi network:
while ( status != WL_CONNECTED) {
Serial.print("Attempting to connect to SSID: ");
Serial.println(ssid);
status = WiFi.begin(ssid, pass);
// wait 10 seconds for connection:
delay(10000);
}
Serial.println("Connected to wifi");
printWifiStatus();
}
void loop() {
int sensorValue = digitalRead(sensorPin);
datastreams[0].setInt(sensorValue);
Serial.print("Read sensor value ");
Serial.println(datastreams[0].getInt());
Serial.println("Uploading it to Xively");
int ret = xivelyclient.put(feed, xivelyKey);
Serial.print("xivelyclient.put returned ");
Serial.println(ret);
Serial.println();
delay(15000);
}

Need help in understanding the mapping of user-space send, sendto, sendmsg to kernel-space sendmsg

I am trying to implement my own transport layer protocol in Linux for an experiment. I am going to use socket interface and add my protocol using sock_register. For the proto_ops i can see that the parameters for the sendmsg and recvmsg are (struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len, int flags). But there are three types of user api's send, sendto, sendmsg. Of these three only sendmsg contains a parameter for msghdr. I find that the other two api's are incompatible with the parameters supplied by the kernel to my kernel-space sendmsg function. So what happens when we use send and sendto user-space api's? Hope i am clear..
Thanks,
Bala
send() is implemented in terms of sendto(): send(s, buf, len, flags); is equivalent to sendto(s, buf, len, flags, NULL, 0);
sendto() is in turn implemented in terms of sendmsg(). send(s, buf, len, flags, addr, addr_len); is implemented with (in terms of the userspace interface):
struct iovec iov = {
.iov_base = buf,
.iov_len = len
};
struct msghdr msg = {
.msg_name = addr,
.msg_namelen = addr_len,
.msg_iov = &iov,
.msg_iovlen = 1,
.msg_control = NULL,
.msg_controllen = 0
};
return sendmsg(s, &msg, flags);
The kernelspace interface is slightly different - eg. you get the kiocb parameter - but the basic idea is the same. A send() or sendto() is converted into a sendmsg() with a msghdr that points at a single iovec that references the buffer.
Assuming you are comfortable with the system call mechanism - start from net/socket.c and follow the call chains - it's more or less clear.

Resources