How to make tap device use socket API? - sockets

I'm using tap device.
The problem is that you just can ->read() or ->write() one packet every system call.
After read the source file tun.c, I found there is struct socket in tun device and function tun_get_socket() can return it.
struct socket *tun_get_socket(struct file *file)
{
struct tun_file *tfile;
if (file->f_op != &tun_fops)
return ERR_PTR(-EINVAL);
tfile = file->private_data;
if (!tfile)
return ERR_PTR(-EBADFD);
return &tfile->socket;
}
EXPORT_SYMBOL_GPL(tun_get_socket);
The socket.ops is set with tun_socket_ops which have ->sendmsg() and ->recvmsg().
static const struct proto_ops tun_socket_ops = {
.sendmsg = tun_sendmsg,
.recvmsg = tun_recvmsg,
.release = tun_release,
};
The questions:
How to create one file descriptor to connect with this socket? That makes me to use ->sendmsg() and ->recvmsg() in userspace. My OS is CentOS7.9 with kernel version 3.10.0-1160.el7.x86_64.
Or is there other ways I can read or write multiple packets at one time from tap device?
Writing new kernel module is acceptable.

Related

Can a BLE Server identify which component is connected to it?

I have this project, where I need to create a BLE-Server (ESP32) to exchange data with a Laptop/Smartphone etc.... . The code is written with Arduino IDE, using the ESP32 BLE Arduino library and seems like the following
void Setup() {
BLEDevice::init("MIDI_BOX"); // Name of the BLE_Device
pServer = BLEDevice::createServer();
pServer->setCallbacks(new MyServerCallbacks());
BLEService *pService = pServer->createService(SERVICE_UUID);
pCharacteristic = pService->createCharacteristic(
MIDI_CHARACTERISTIC_UUID,
BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_WRITE |
BLECharacteristic::PROPERTY_NOTIFY |
BLECharacteristic::PROPERTY_WRITE_NR
);
pCharacteristic->addDescriptor(new BLE2902());
pCharacteristic->setCallbacks(new MyCallbacks());
pService->start();
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
pAdvertising->addServiceUUID(SERVICE_UUID);
pAdvertising->setScanResponse(true);
pAdvertising->setMinPreferred(0x0); // set value to 0x00 to not advertise this parameter
BLEDevice::startAdvertising();
};
Having this said, when I want to connect my Laptop or Smartphone to this server. It needs to indetify which kind of device is connected to(Apple, Microsoft,). The manufacturer data seems a good idea for me but I don't how to get this information when there is a connection.
A callback fonction is provided by the BLEServer:
class MyServerCallbacks: public BLEServerCallbacks { //can I here do smth to get manufacturer data ??
void onConnect(BLEServer* pServer) {
deviceConnected = true;
};
void onDisconnect(BLEServer* pServer) {
deviceConnected = false;
}
};
Can anyone explain to me how it should be done, or are there any better way of thinking ?
Thnx in advance

NETunnelProvider stop receiving packet on iOS 14?

I'm having a Local VPN app that using "NETunnelProvider / NetworkExtentsion", In my solution, I created a split tunnel on the device itself to track the DNS request, using NEKit I was able to peek inside the packets and filter the ongoing request based on the destination address (let's call ita UDP listener for DNS requests).
This solution was working fine on iOS 13.7 and less, recently apple release iOS 14, and my solution stop working, VPN connection still established but the user can't access any webSite, I debugged the code and found out the networkExtision does not receive any packets from user activity only.
I'm using the CocoaAsyncSocket library.
func udpSocket(_ sock: GCDAsyncUdpSocket, didReceive data: Data, fromAddress address: Data, withFilterContext filterContext: Any?) {
let host = GCDAsyncUdpSocket.host(fromAddress: address)
guard let message = DNSMessage(payload: data) else {
return
}
guard let session = pendingSession.removeValue(forKey: message.transactionID) else {
return
}
session.realResponseMessage = message
session.realIP = message.resolvedIPv4Address
let domain = session.requestMessage.queries[0].name
let udpParser = UDPProtocolParser()
udpParser.sourcePort = Port(port: dnsServerPort)
udpParser.destinationPort = (session.requestIPPacket!.protocolParser as! UDPProtocolParser).sourcePort
udpParser.payload = session.realResponseMessage!.payload
let ipPacket = IPPacket()
ipPacket.sourceAddress = IPAddress(fromString: dnsServerAddress)
ipPacket.destinationAddress = session.requestIPPacket!.sourceAddress
ipPacket.protocolParser = udpParser
ipPacket.transportProtocol = .udp
ipPacket.buildPacket()
packetFlow.writePackets([ipPacket.packetData], withProtocols: [NSNumber(value: AF_INET as Int32)])
}
let dummyTunnelAddress = "127.0.0.1"
let dnsServerAddress = "8.8.4.4"
let dnsServerPort: UInt16 = 53
// Tunnel confg.
let tunnelAddress = "192.168.0.1"
let tunnelSubnetMask = "255.255.255.0"
Regarding triggering"Local Network permissions" which is not the issue here (I don't think my solution need to have this permission), Based on the apple document some apps need to request local network permissions, I added the permission to the info.plist but local network permissions are not triggered.
==========================
Update #1
============================
I found out that I was able to capture the packets and do my own things then write packets back to the packetFlow packetFlow.writePackets, But on iOS 14 browsers not loading the websites and keep loading until show time out.
I have an idea, and maybe a solution for you. Starting in iOS version 14, the C function connect() started failing in network extensions, where VPNs have to run, with the following log message from the kernel:
Sandbox: VPN Extensio(8335) deny(1) network-outbound*:<port #>
However, it does work in the app, which is next to useless if you need it in the extension. GCDAsyncUdpSocket is a thin layer that right under the covers is calling socket() and connect().
NWConnection does work in a network extension, and it should work for you if it is feasible to port your code and you don't need the actual socket descriptor. But you will have to conditionally compile if you have to support devices < ios 12.

Write to HID with Chip Selection with .NET Console App

Hi I am writing a simple console app that needs to write bytes to MCP2210 USB to SPI Master
I found this library over here, seems to do good job with connecting the device and reading the metadata.
I am writing message to the board as below
public static byte[] Talk()
{
var device = DeviceList.Local.GetHidDevices(1240, 222).FirstOrDefault();
if (device == null)
{
Console.WriteLine($"Could not find a device with Vendor Id:1240, Product Id:222 ");
return null;
}
var reportDescriptor = device.GetReportDescriptor();
foreach (var deviceItem in reportDescriptor.DeviceItems)
{
Console.WriteLine("Opening device for 20 seconds...");
if (!device.TryOpen(out var hidStream))
{
Console.WriteLine("Failed to open device.");
continue;
}
Console.WriteLine("Opened device.");
hidStream.ReadTimeout = Timeout.Infinite;
hidStream.Write(new byte[3] {60, 00, 00});
}
Not sure If I am writing it correctly.
While writing I need to do a chip selection as displayed in this other terminal
Any help is greatly appreciated
Here is the MC I am using https://www.microchip.com/wwwproducts/en/MCP2210
I do not see a closing of your stream. This may cause your data to not even being sent (at least not in time).
Consider using blocks with streams.
But with out parameters not possible.

How to performs I/O to block device from block device driver under Linux

I have a task to write a block device driver (/dev/dua - for example) , this block device is must be looks like to OS as a disk device like /dev/sda. So, this driver must process data blocks and write it to other block device.
I looking for a right way to performs I/O operations on the backend device like "/dev/sdb".
I have played with the vfs_read/write routines it's works at glance for disk sector sized transfers. But, probably there is more effective way to performs I/O on backend device ?
TIA.
Follows a piece of code (original has been found here : https://github.com/asimkadav/block-filter) implements a "filtering" feature, so it can be used as a method to performs I/O on a backend block device `
void misc_request_fn(struct request_queue *q, struct bio *bio) {
printk ("we are passing bios.\n");
// here is where we trace requests...
original_request_fn (q, bio);
return;
}
void register_block_device(char *path) {
struct request_queue *blkdev_queue = NULL;
if (path == NULL) {
printk ("Block device empty.\n");
return;
}
printk ("Will open %s.\n", path);
blkdev = lookup_bdev(path);
if (IS_ERR(blkdev)) {
printk ("No such block device.\n");
return;
}
printk ("Found block device %p with bs %d.\n", blkdev, blkdev->bd_block_size);
blkdev_queue = bdev_get_queue(blkdev);
original_request_fn = blkdev_queue->request_fn;
blkdev_queue->request_fn = misc_request_fn;
}
`

Send packet with sockets from kernel module

I am writing a kernel module that should receive messages from user-space and send response back via socket.
When program and module are on the same machine and I use IP 127.0.0.1, everything works fine. But when I try it on different machines and use real network IP, something like 192.168.3.146 it works only in one way.
I receive message from user-space, but I can not receive it from kernel. I use sock_sendmsg function for sending message from kernel and it's not return any error. Also I am not get any messages from firewall that something is came up from another machine, from kernel module.
Here were similar questions and examples, but they were not useful enough for me or examples were used too old kernel version.For skeleton I used this one,from UDP sockets: http://people.ee.ethz.ch/~arkeller/linux/multi/kernel_user_space_howto-3.html. Any help?
Kernel module code for sending:
void send_data(unsigned char *data)
{
if(!IS_ERR_OR_NULL(data))
{
int ret;
mm_segment_t oldfs;
struct msghdr message;
struct iovec ioVector;
struct sockaddr_in sendAddr;
sendAddr.sin_family = AF_INET;
sendAddr.sin_addr.s_addr = INADDR_ANY;
//sendAddr.sin_addr.s_addr = in_aton("192.168.1.75");
//here I get port from sk_buff structure that I received.
sendAddr.sin_port = *((unsigned short*)skBuffer->data);
memset(&message, 0, sizeof(message));
message.msg_name = &sendAddr;
message.msg_namelen = sizeof(sendAddr);
/* send the message back */
ioVector.iov_base = data;
ioVector.iov_len = strlen(data);
message.msg_iov = &ioVector;
message.msg_iovlen = 1;
message.msg_control = NULL;
message.msg_controllen = 0;
oldfs = get_fs();
set_fs(KERNEL_DS);
ret = sock_sendmsg(sendSocket, &message, strlen(data));
set_fs(oldfs);
}
}
I found an alternative solution, using netpoll sockets. It is more easier than sockets, I used before and it works. The answer and proper code is here, on another StackOverflow question.