How can I send more bytes using Bluez L2CAP? - linux

I have the following code that connects to another machine using BlueZ and sends packets:
struct sockaddr_l2 addr = { 0 };
int s, status;
char dest[18] = "DC:FB:48:6B:BF:0B";
int socket1;
int32_t value = 0;
// allocate a socket
socket1 = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
// set the connection parameters (who to connect to)
addr.l2_family = AF_BLUETOOTH;
addr.l2_psm = htobs(0x1001);
str2ba( dest, &addr.l2_bdaddr );
status = connect(socket1, (struct sockaddr *)&addr, sizeof(addr));
while( status != 0)
{
status = connect(socket1, (struct sockaddr *)&addr, sizeof(addr));
std::cout << "Waiting for DC:FB:48:6B:BF:0B" << std::endl;
sleep(2);
}
while (true)
{
double data[512] = {0.0};
memset(data, 0, 512);
status = write(socket1, data, 512);
}
As you can see, I just send 512 bytes and other machine reads them just fine. However, when I try to increase to 1000 bytes, other machine can no longer accept any bytes and just does nothing.
How can I send more bytes in this case? I am using Linux CentOS 8.

By default the maximum transmission rate of L2CAP is 672 bytes. I would recommend you to try setting the maximum transmission unit to your required value.
Have a look at "4.3.1 Maximum Transmission Unit" here.

Related

Select MAC address for binding to a specific bluetooth adapter

The following piece of code binds a Bluetooth socket. My expectation is that the call to bind() (or to listen()) with that MAC address as argument fails, because when using AF_INET sockets, I cannot bind() to an IP address that I don't have. This answer suggests that it's possible to select your adapter, but I cannot reproduce that in my experiments.
/* From https://people.csail.mit.edu/albert/bluez-intro/x502.html */
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/rfcomm.h>
int main(int argc, char **argv)
{
struct sockaddr_rc loc_addr = { 0 }, rem_addr = { 0 };
char buf[1024] = { 0 };
int s, client, bytes_read;
socklen_t opt = sizeof(rem_addr);
// allocate socket
s = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
// bind socket to port 1 of the first available
// local bluetooth adapter
loc_addr.rc_family = AF_BLUETOOTH;
// loc_addr.rc_bdaddr = *BDADDR_ANY;
bdaddr_t my_bdaddr = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06};
loc_addr.rc_bdaddr = my_bdaddr;
loc_addr.rc_channel = (uint8_t) 1;
{
int res = bind(s, (struct sockaddr *)&loc_addr, sizeof(loc_addr));
char baddr[128];
ba2str(&loc_addr.rc_bdaddr, baddr);
fprintf(stderr, "bound to %s: %d\n", baddr, res);
}
// put socket into listening mode
{
int res = listen(s, 1);
fprintf(stderr, "Listening: %d\n", res);
}
// accept one connection
client = accept(s, (struct sockaddr *)&rem_addr, &opt);
ba2str( &rem_addr.rc_bdaddr, buf );
fprintf(stderr, "accepted connection from %s\n", buf);
memset(buf, 0, sizeof(buf));
// read data from the client
bytes_read = read(client, buf, sizeof(buf));
if( bytes_read > 0 ) {
printf("received [%s]\n", buf);
}
// close connection
close(client);
close(s);
return 0;
}
So I guess my question is: Is my expectation of the bind() having to fail wrong?
The main reason I am concerned with the MAC address is that I want another party to connect to me and send it some data. For the other party to establish a connection, it needs my MAC address and port. Now I can find my MAC address via the DBus API (or probably via some other means) but then I'm afraid that it's racy, e.g. I cannot be sure that the MAC address I queried is the same that I've bound to, because in the time between the query and the bind the user may have removed their Bluetooth adapter and inserted a new one. And before I jump through the hoops of subscribing to BlueZ DBus signals I thought I'd use the MAC for binding the socket.

Handsfree device not able to receive any audio from sco socket

I am developing a handsfree module, in which after completing service level connection I am setting up sco connection with phone's audio gateway and receive audio data as below...
void audio_connection_setup(char *bluetooth_addr)
{
struct sockaddr_sco addr;
pthread_t tid;
int scoSock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
memset(&addr, 0, sizeof(addr));
addr.sco_family = AF_BLUETOOTH;
addr.sco_bdaddr = *BDADDR_ANY;
if (bind(scoSock, (struct sockaddr *) &addr, sizeof(addr)) < 0)
{
printf( "Can't bind socket: %s (%d)",strerror(errno), errno);
}
hci_read_voice_setting(scoSock, &voice, 5000);
hci_write_voice_setting(scoSock, BT_VOICE_CVSD_16BIT, 5000);
str2ba(bluetooth_addr, &addr.sco_bdaddr);
if (connect(scoSock, (struct sockaddr *) &addr, sizeof(addr)) < 0)
{
printf( "\nCan't connect: %s (%d)", strerror(errno), errno);
}
pthread_create(&tid, NULL, &read_data, &scoSock);
}
Here is the read_data thread
gBuff[16284];
void* read_data(int *scoSock)
{
int fd = *scoSock;
int len = -1;
char buff[48];
int numBytesRead;
while (1)
{
numBytesRead = 0;
while(numBytesRead < 16284)
{
memset(buff, 0x0, 48);
len = recv(fd, buff, 48, MSG_DONTWAIT);
usleep(10);
memcpy(gBuff + numBytesRead, buff + 2, len - 2);
numBytesRead = numBytesRead + len - 2;
}
printf("Number of bytes received = %d errno = %d\n", numBytesRead, errno);
memset(gBuff, 0x0, numBytesRead);
}
}
This code is working fine if I run it on linux PC, But when i run on arm board the recv system call returns errno EAGAIN in continuous loop and never comes out. On PC the recv system call returns number of bytes properly. What may be possible cause to this issue?
I think the reason should be when your code runs on the pc, you may get the audio data from HCI link, i.e. the HF sco over HCI and then you transfer it to PC driver. However when you switch to board, I wonder whether the board's hardware Bluetooth interface lets say UART, whether it has high throughput to transfer the sco data, and most importantly, you need check whether the sco data was routed to PCM interface i.e. does not sending to HCI.

Two processes share UDP socket, one stops receiving messages after being restarted

On a Linux platform I want to use socket sharing between 2 processes. One of the processes sends data on the socket, the other one receives data. I read on this site (here) that this is done by setting an option SO_REUSEADDR and/or SO_REUSEPORT.
So I set a test scenario with 3 processes:
1) An echo server bound to localhost that listens for messages on 127.0.0.1:44000. When it receives a message it replies to the sender immediately;
2) A sender that binds to 127.0.01:44001 and issues periodic messages to the echo server;
3) A receiver that binds to 127.0.01:44001 and listens for messages from the echo server;
The problem: Receiver stops receiving replies from the echo server. This depends on the socket option used:
With SO_REUSEADDR:
If sender(2) is started after the receiver(3) the latter does not receive anything. If the receiver is started last, but the sender is restarted, again the receiver stops receiving.
With SO_REUSEPORT (or together with SO_REUSEADDR):
The situation is just the opposite - receiver has to be started first for things to work, as sender is started last you can restart sender as many times as you want, everything will work well. But if you restart the sender (or just start it last) it will not get any message.
This is the code I use:
#define CC_LISTEN_PORT 44000
#define DRN_LISTEN_PORT 44001
static void runCC_EchoMode(struct sockaddr_in* ccaddr)
{
char buf[100];
int s = socket(AF_INET, SOCK_DGRAM, 0);
struct sockaddr_in remaddr;
int recvlen, sentlen;
// bind
if(bind(s, (struct sockaddr *)ccaddr, sizeof(struct sockaddr_in)) < 0)
{
debug("%s: bind failed", __func__);
return;
}
/* now loop, receiving data and printing what we received */
unsigned int addrlen = sizeof(remaddr);
int count = 0;
for (;;) {
debug("waiting on port %d\n", ntohs(ccaddr->sin_port));
recvlen = recvfrom(s, buf, sizeof(buf), 0, (struct sockaddr *)&remaddr, &addrlen);
debug("received %d bytes\n", recvlen);
if (recvlen > 0) {
buf[recvlen] = 0;
printf("received message: \"%s\"\n", buf);
// send echo back
sprintf(buf, "Echo #%d", count++);
sentlen = sendto(s, buf, strlen(buf), 0, (struct sockaddr *)&remaddr, sizeof(remaddr));
debug("sent %d bytes to %s:%d\n", sentlen,
inet_ntoa(remaddr.sin_addr), ntohs(remaddr.sin_port));
}
}
close(s);
}
static void runDrn_SendMode(struct sockaddr_in* ccaddr, struct sockaddr_in* drnaddr)
{
char buf[100];
int s = socket(AF_INET, SOCK_DGRAM, 0);
int sentlen;
int one = 1;
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(int)) < 0) {
debug("setsockopt(SO_REUSEADDR) failed\n");
}
if (setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(int)) < 0) {
debug("setsockopt(SO_REUSEPORT) failed\n");
}
// bind
if(bind(s, (struct sockaddr *)drnaddr, sizeof(struct sockaddr_in)) < 0)
{
debug("%s: bind failed", __func__);
return;
}
int count = 0;
for (;;) {
sleep(2);
sprintf(buf, "Hello #%d", count++);
debug("sending \"%s\" to server...\n", buf);
sentlen = sendto(s, buf, strlen(buf), 0, (struct sockaddr *)ccaddr, sizeof(struct sockaddr_in));
debug("sent %d bytes\n", sentlen);
}
close(s);
}
static void runDrn_RcvMode(struct sockaddr_in* ccaddr, struct sockaddr_in* drnaddr)
{
char buf[100];
int s = socket(AF_INET, SOCK_DGRAM, 0);
struct sockaddr_in remaddr;
int recvlen;
int one = 1;
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(int)) < 0) {
debug("setsockopt(SO_REUSEADDR) failed\n");
}
if (setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(int)) < 0) {
debug("setsockopt(SO_REUSEPORT) failed\n");
}
// bind
if(bind(s, (struct sockaddr *)drnaddr, sizeof(struct sockaddr_in)) < 0)
{
debug("%s: bind failed", __func__);
return;
}
/* now loop, receiving data and printing what we received */
unsigned int addrlen = sizeof(remaddr);
for (;;) {
debug("waiting on port %d\n", ntohs(drnaddr->sin_port));
recvlen = recvfrom(s, buf, sizeof(buf), 0, (struct sockaddr *)&remaddr, &addrlen);
debug("received %d bytes\n", recvlen);
if (recvlen > 0) {
buf[recvlen] = 0;
printf("received message: \"%s\"\n", buf);
}
}
close(s);
}
int main(int argc, char *argv[])
{
int mode;
if (argc < 3) {
fprintf(stderr, "Usage: %s <host> <mode>\n", argv[0]);
exit(EXIT_FAILURE);
}
printf("Starting process with PID: %d\n", getpid());
// this is a simple wrapper of getaddrinfo()
AddressResolver resv1(argv[1]);
resv1.print();
struct sockaddr_in ccaddr, drnaddr;
ccaddr = *(resv1.getAddress(0));
ccaddr.sin_port = htons(CC_LISTEN_PORT);
drnaddr = *(resv1.getAddress(0));
drnaddr.sin_port = htons(DRN_LISTEN_PORT);
mode = atoi(argv[2]);
switch(mode) {
case 0: // cc
runCC_EchoMode(&ccaddr);
break;
case 1: // drone sender
runDrn_SendMode(&ccaddr, &drnaddr);
break;
case 2: // drone receiver
runDrn_RcvMode(&ccaddr, &drnaddr);
break;
default:
debug("Mode is not available\n");
break;
}
return 0;
}
And this is how I start the 3 processes:
./testUDP localhost 0
./testUDP localhost 1
./testUDP localhost 2
This is a the output of a test run:
./testUDP localhost 0
Starting process with PID: 10651
IP: 127.0.0.1
waiting on port 44000
received 8 bytes
received message: "Hello #0"
sent 7 bytes to 127.0.0.1:44001
waiting on port 44000
received 8 bytes
received message: "Hello #1"
sent 7 bytes to 127.0.0.1:44001
waiting on port 44000
received 8 bytes
received message: "Hello #2"
sent 7 bytes to 127.0.0.1:44001
waiting on port 44000
^C
...
./testUDP localhost 1
Starting process with PID: 10655
IP: 127.0.0.1
sending "Hello #0" to server...
sent 8 bytes
sending "Hello #1" to server...
sent 8 bytes
sending "Hello #2" to server...
sent 8 bytes
^C
...
./testUDP localhost 2
Starting process with PID: 10652
IP: 127.0.0.1
waiting on port 44001
received 7 bytes
received message: "Echo #0"
waiting on port 44001
received 7 bytes
received message: "Echo #1"
waiting on port 44001
received 7 bytes
received message: "Echo #2"
waiting on port 44001
^C
The behavior of two different processes listening on the same interface and port is non-deterministic: it will vary by operating system, kernel version, and other factors.
In general, a port is intended to be associated with a single process and socket. SO_REUSE is intended for UDP multicast receive or for TCP to bypass the WAIT state after a connection drop. While some systems will let you bind a single port to multiple sockets, threads, or processes for other purposes, the behavior is too varied to be useful.
What you are probably looking for is some kind of packet duplication or round-robin distribution. SO_REUSE does not guarantee that.

packet mmap send packet format

I want to improve packet transmitting performance. Before that I used raw sockets and now I study packet_mmap.
I have packets(frames) which I already captured from kernel module from another PC, put to current PC and now I want to retransmit them to local interface with following forwarding.
I have got example of packet_mmap, integrated it to my project, but I send
fd_socket = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
memset(&my_addr, 0, sizeof(struct sockaddr_ll));
my_addr.sll_family = PF_PACKET;
my_addr.sll_protocol = htons(ETH_P_ALL);
strcpy(str_devname, "eth0");
strncpy(s_ifr.ifr_name, str_devname, sizeof(s_ifr.ifr_name));
ec = ioctl(fd_socket, SIOCGIFINDEX, &s_ifr);
i_ifindex = s_ifr.ifr_ifindex;
memset(&my_addr, 0, sizeof(struct sockaddr_ll));
my_addr.sll_family = AF_PACKET;
my_addr.sll_protocol = ETH_P_ALL;
my_addr.sll_ifindex = i_ifindex;
bind(fd_socket, (struct sockaddr *) &my_addr, sizeof(struct sockaddr_ll)
s_packet_req.tp_block_size = c_buffer_sz;
s_packet_req.tp_frame_size = c_buffer_sz;
s_packet_req.tp_block_nr = c_buffer_nb;
s_packet_req.tp_frame_nr = c_buffer_nb;
size = s_packet_req.tp_block_size * s_packet_req.tp_block_nr;
if (setsockopt(fd_socket, SOL_PACKET, PACKET_TX_RING, (char *) &s_packet_req, sizeof(s_packet_req)) < 0) {
perror("setsockopt: PACKET_TX_RING");
return;
}
if (c_sndbuf_sz) {
printf("send buff size = %d\n", c_sndbuf_sz);
if (setsockopt(fd_socket, SOL_SOCKET, SO_SNDBUF, &c_sndbuf_sz, sizeof(c_sndbuf_sz)) < 0){
perror("getsockopt: SO_SNDBUF");
exit(1);
}
}
data_offset = TPACKET_HDRLEN - sizeof(struct sockaddr_ll);
printf("data offset = %d bytes\n", data_offset);
ps_header_start = (tpacket_hdr *) mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_socket, 0);
if (ps_header_start == (void*) -1) {
perror("mmap");
exit(1);
}
Then I fill data
ps_header = ((struct tpacket_hdr *) ((char *) ps_header_start + (c_buffer_sz * i_index)));
if (!ps_header) {
perror("ps_header") ;
return NULL;
}
data = ((char*) ps_header) + data_offset;
switch ((volatile uint32_t) ps_header->tp_status) {
case TP_STATUS_AVAILABLE:
printf("TP_STATUS_AVAILABLE, index=%d\n",i_index) ;
memcpy(data, packet_data, size);
pthread_mutex_lock(&index_locker) ;
i_index++;
pthread_mutex_unlock(&index_locker) ;
if (i_index >= c_buffer_nb) {
i_index = 0;
first_loop = 0;
}
/* update packet len */
ps_header->tp_len = size;
/* set header flag to USER (trigs xmit)*/
ps_header->tp_status = TP_STATUS_SEND_REQUEST;
then I send
ec_send = sendto(fd_socket, NULL, 0, 0, (struct sockaddr *) ps_sockaddr, sizeof(struct sockaddr_ll));
I have got no errors, ec_send=not null size of sended data. But there are no data routed to destination host.
So, I ask
- what data I should pass to ring buffer, now I include headers ip,tcp, should I include MAC header?
- May be I have to set additional flags to route my packets.
You are creating socket using SOCK_RAW. It means that you are in authority to provide Layer-2 information (Ethernet/MAC address). You can create socket with SOCK_DGRAM. Kernel will handle Layer-2 information to put next hop's Ethernet address. Try that.

Question about INADDR_ANY

The constant INADDR_ANY is the so-called IPv4 wildcard address. The
wildcard IP address is useful for applications that bind Internet
domain sockets on multihomed hosts. If an application on a multihomed
host binds a socket to just one of its host’s IP addresses, then that
socket can receive only UDP datagrams or TCP connection requests sent
to that IP address. However, we normally want an application on a
multihomed host to be able to receive datagrams or connection requests
that specify any of the host’s IP addresses, and binding the socket to
the wildcard IP address makes this possible.
struct sockaddr_in server_address;
int server_sockfd = socket(AF_INET, SOCK_STREAM, 0);
memset(&server_address, 0, sizeof(struct sockaddr_in));
server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = htonl(INADDR_ANY); // here is my quesion
server_address.sin_port = htons(9734);
bind(server_sockfd, (struct sockaddr*)&server_address, sizeof(server_address));
Question>
If we bind the socket to a specific IP address, then the socket can only receive UPD/TCP requests sent sent to that IP address.
As I show in the above code, now the socket server_sockfd is bound with INADDR_ANY.
I just feel confused here b/c if the socket can receive any request on the internet, how it can still work well. There are tons of requests of UDP/TCP on internet, if the socket responses to everybody,
, how can it still work?
// updated code for client side //
int
main(int argc, char *argv[])
{
struct sockaddr_in6 svaddr;
int sfd, j;
size_t msgLen;
ssize_t numBytes;
char resp[BUF_SIZE];
if (argc < 3 || strcmp(argv[1], "--help") == 0)
usageErr("%s host-address msg...\n", argv[0]);
/* Create a datagram socket; send to an address in the IPv6 somain */
sfd = socket(AF_INET6, SOCK_DGRAM, 0); /* Create client socket */
if (sfd == -1)
errExit("socket");
memset(&svaddr, 0, sizeof(struct sockaddr_in6));
svaddr.sin6_family = AF_INET6;
svaddr.sin6_port = htons(PORT_NUM);
if (inet_pton(AF_INET6, argv[1], &svaddr.sin6_addr) <= 0)
fatal("inet_pton failed for address '%s'", argv[1]);
/* Send messages to server; echo responses on stdout */
for (j = 2; j < argc; j++) {
msgLen = strlen(argv[j]);
if (sendto(sfd, argv[j], msgLen, 0, (struct sockaddr *) &svaddr,
sizeof(struct sockaddr_in6)) != msgLen)
fatal("sendto");
numBytes = recvfrom(sfd, resp, BUF_SIZE, 0, NULL, NULL);
if (numBytes == -1)
errExit("recvfrom");
printf("Response %d: %.*s\n", j - 1, (int) numBytes, resp);
}
exit(EXIT_SUCCESS);
}
// updated for server side code
int
main(int argc, char *argv[])
{
struct sockaddr_in6 svaddr, claddr;
int sfd, j;
ssize_t numBytes;
socklen_t len;
char buf[BUF_SIZE];
char claddrStr[INET6_ADDRSTRLEN];
/* Create a datagram socket bound to an address in the IPv6 somain */
sfd = socket(AF_INET6, SOCK_DGRAM, 0);
if (sfd == -1)
errExit("socket");
memset(&svaddr, 0, sizeof(struct sockaddr_in6));
svaddr.sin6_family = AF_INET6;
svaddr.sin6_addr = in6addr_any; /* Wildcard address */
svaddr.sin6_port = htons(PORT_NUM);
if (bind(sfd, (struct sockaddr *) &svaddr,
sizeof(struct sockaddr_in6)) == -1)
errExit("bind");
/* Receive messages, convert to uppercase, and return to client */
for (;;) {
len = sizeof(struct sockaddr_in6);
numBytes = recvfrom(sfd, buf, BUF_SIZE, 0,
(struct sockaddr *) &claddr, &len);
if (numBytes == -1)
errExit("recvfrom");
/* Display address of client that sent the message */
if (inet_ntop(AF_INET6, &claddr.sin6_addr, claddrStr,
INET6_ADDRSTRLEN) == NULL)
printf("Couldn't convert client address to string\n");
else
printf("Server received %ld bytes from (%s, %u)\n",
(long) numBytes, claddrStr, ntohs(claddr.sin6_port));
for (j = 0; j < numBytes; j++)
buf[j] = toupper((unsigned char) buf[j]);
if (sendto(sfd, buf, numBytes, 0, (struct sockaddr *) &claddr, len) !=
numBytes)
fatal("sendto");
}
}
// updated for how to run this server/client programs.
$ ./server_program &
[1] 31047
$ ./client_program ::1 ciao // Send to server on local host
Server received 4 bytes from (::1, 32770)
Response 1: CIAO
It doesn't get requests for every IP address on the internet(a), it gets requests for every IP address that it services. For example, it may have multiple NICs, each with a separate IP address or it may have a single NIC capable of managing multiple IP addresses (it may even have multiple NICs, each capable of handling multiple IP addresses.
The key snippet to look at is:
... we normally want an application on a multi-homed host to be able to receive datagrams or connection requests that specify any of the host’s IP addresses (my italics).
In other words, you may have a multi-homed set-up where your machine services 10.0.0.15 and 10.0.0.16. Using INADDR_ANY will allow you to pick up traffic for both those addresses, without picking up requests for 10.0.0.17 which may be the machine on the other end of the bench (or other side of the planet).
The following table, with the top row being request destinations and the left column being the address you're listening on, shows whether you'll be given a request (Y) or not (N):
Request to> 10.0.0.15 10.0.0.16 10.0.0.17
Bind to: *-------------------------------
10.0.0.15 | Y N N
10.0.0.16 | N Y N
INADDR_ANY | Y Y N
(a) It doesn't even see the vast majority of requests on the net. The vast majority don't even make it to your nearest router (or probably even your ISP). Even those that do make it to your nearest router, your particular machine might not see if they're destined for another machine on the local segment (promiscuous mode notwithstanding).

Resources