char device catch multiple (int) ioctl-arguments - c

I have to write a linux char device, which handles ioctl (without BKL) functions per unlock_ioctl. At the moment I can receive one argument from the userspace ioctl command per
__get_user(myint, (int __user *) arg);
How can I receive multiple int-arguments (for example this call)?:
ioctl(fp, SZ_NEW_DEV_FORMAT, 0, 1, 30);

Yes, you have to use structures. For a particular ioctl command there will be some predefined arguments. You need to wrap these all arguments into a structure object and pass in the address of the object. In side the kernel, you need to type cast the given arg to structure pointer and access the arguments. For instance.
struct mesg {
int size;
char buf[100];
};
struct mesg msg1;
/*Fill in the structure object here and call ioctl like this*/
ret = ioctl(fd, SZ_NEW_DEV_FORMAT, &msg1);
Inside the kernel you access it like this:
struct mesg *msg;
copy_from_user((char *)msg, (char *)arg, sizeof(*msg));

Related

Pass struct to xv6 system call

I'm aware that we are not able to pass parameters to xv6 system call directly and we are forced to use it's built in methods.
But all examples and questions in this site is about how to send integer to system call. Which it's answer is using argint() method.
But my question is, is there anyway to pass "struct" to a xv6 system call? Are there any bulit-in methods for this purpose too?
If there is, could you please say a simple example?
Passing a struct through system call is possible.
While one can't pass a struct itself as a system call parameter, passing a pointer to it is possible and will allow using it as both an input or output parameter.
Allowing to use as argument the data itself and not a pointer to it will damage the requirement of the system calls mechanism- as passing data must be implemented in a generic way to allow all data types to (as well as future structs) be used.
Let's have a look on an existing implementation of the system call fstat.
int fstat(int fd, struct stat *st);
fstat requires a file descriptor number as an input and outputs a matching stats information using struct stat.
struct stat {
short type; // Type of file
int dev; // File system's disk device
uint ino; // Inode number
short nlink; // Number of links to file
uint size; // Size of file in bytes
};
Although fstat uses a struct pointer as an output parameter, using it as an input will be similar.
The function sys_fstat in kernel code starts the implementation of fstat system call (XV6's convention is to handle parameter fetching from user space by sys_* functions).
int sys_fstat(void)
{
struct file *f;
struct stat *st;
if(argfd(0, 0, &f) < 0 || argptr(1, (void*)&st, sizeof(*st)) < 0)
return -1;
return filestat(f, st);
}
This function first gets a corresponding struct file to the file descriptor number received by the first fstat function argument (using argfd). Then, fetches the struct stat pointer received by the second fstat function argument using argptr and saves the given pointer in a local (function scope) pointer variable.
At this point, all arguments were fetched and can be used by the kernel implementation.
Note: Although the struct stat pointer is a user-space pointer (located on the lower half of the virtual space), it is safe for the kernel to use it here because when the kernel is serving a process' system call, it uses the process' own paging table.
Although the above answer is correct but i prefered to write my own solutions to make it more usable for other viwers.
i used argptr to pass a pointer-to-struct to my system call.
in sysproc.c:
int sys_counts (void){
struct countTable *ct;
argptr (0 , (void*)&ct ,sizeof(*ct));
return counts(ct);
}
in proc.c:
int counts (struct countTable *ct){
for (int i=0 ; i<22 ; i++){
(ct->system_calls)[i] = count_calls[i] ;
}
return 22;
}
and finally in my user-space-program:
int main (){
struct countTable *ct = malloc (sizeof (struct countTable));
// call system call
counts(ct);
exit();
}
Although one of the answers is acceptable I wrote my answer that is clear and complete.
Note that passing an argument to system-call directly is impossible. we will use argptr to do that.
In userspace, we define a struct that we want to work with. in a user-level file like test.c
#include "types.h"
#include "stat.h"
#include "user.h"
struct Data
{
...
int id; // some fields
...
};
int main(int argc, char *argv[])
{
struct Data *data = malloc(sizeof(struct Data));
// call the systemcall
doSomeWork((void *)data);
exit();
}
In sysproc.c we define system-call and use argptr to get arguments:
int sys_doSomeWork(void){
struct Data *data;
argptr(0, (void *)&data, sizeof(*data));
return doSomeWork((void *)data);
}
and in proc.c we can write the functionality of system-call:
int doSomeWork(void *data){
// cast to (struct Data *)
struct Data *my_data = (struct Data *)data;
...
// work with my_data
...
return 0;
}
and to make Data struct accessible inside sysproc.c and proc.c we define Data struct inside defs.h:
struct Data
{
...
int id; // some fields
...
};

Local socket option set vs pointer to socket option set

I wonder... what is the actual difference if you create a function that has a simple socket parameter and you do your basic instructions inside that function like setting different option to that socket (setsockopt()) and after the functions exist it will remain the option? Or should I make that parameter pointer to that socket in order to keep the actual changes that will happen to the socket.
sctp_enable_events( int socket, int ev_mask )
{
struct sctp_event_subscribe ev;
bzero(&ev, sizeof(ev));
if (ev_mask & SCTP_SNDRCV_INFO_EV)
ev.sctp_data_io_event = 1;
/*code */
if (setsockopt(socket,
IPPROTO_SCTP,
SCTP_EVENTS,
SCTP_SET_EVENTS,
(const char*)&ev,
sizeof(ev)) != 0 ) {
fprintf(where,
"sctp_enable_event: could not set sctp events errno %d\n",
errno);
fflush(where);
exit(1);
}
}
Or like this?
sctp_enable_events( int *socket, int ev_mask, struct sctp_event_subscribe *ev )
{
if (ev_mask & SCTP_SNDRCV_INFO_EV)
ev->sctp_data_io_event = 1;
/*code */
if (setsockopt(*socket,
IPPROTO_SCTP,
SCTP_EVENTS,
SCTP_SET_EVENTS,
ev,
sizeof(*ev)) != 0 ) {
fprintf(where,
"sctp_enable_event: could not set sctp events errno %d\n",
errno);
fflush(where);
exit(1);
}
}
I know by passing pointer to struct,int,char etc. you cand modify the values after the function executes and without a pointer the modification will remain local in that function only ,but it will not change it globally.
But how with the setsockopt function?
The setsockopt function can't tell how you come up with it's arguments. Therefore, it cannot act differently. Whether you write:
f(1);
or
int x = 1;
int* xPtr = &x;
f(*xPtr);
is not detectable by f.
This question really has nothing to do with setsockopt. I advise you to strengthen your C knowledge around pointers and values. Without this understanding you are going to make many mistakes in C.
You should generally pass pointers to structs because of the efficiency and because you may want to use those structs (by dereferencing the pointer) in different contexts.
In the case of the socket parameter of the setsockopt(), it is an int, so it is small enough to be passed by value (also the signature of the setsockopt() function requires an int, not a pointer).
As for setsockopt() and other C system API functions, you should lookup its declarations/prototypes and provide the parameters of exactly the same type as the prototype requires (doing the casting if neccessary).
In the case of setsockopt() it would be:
int `setsockopt()` setsockopt(int sockfd, int level, int optname,
const void *optval, socklen_t optlen);

Understanding the msghdr structure from sys/socket.h

I'm trying to understand the following members of the msghdr structure of the sys/socket.h lib.
struct iovec *msg_iov scatter/gather array
void *msg_control ancillary data, see below
It states below:
Ancillary data consists of a sequence of pairs, each consisting of a cmsghdr structure followed by a data array. The data array contains the ancillary data message, and the cmsghdr structure contains descriptive information that allows an application to correctly parse the data.
I'm assuming the msghdr struct, contains the protocol-header information? if so... *msg_iov is the input/output "vector" of parameters in the request/response? and the *msg_control contains the response messages?
msg_iov is an array of input/output buffers with length msg_iovlen. Each member of this array contains a pointer to a data buffer and the size of the buffer. This is where the data to read/write lives. It allows you to read/write to an array of buffers which are not necessarily in contiguous memory regions.
msg_control points to a buffer of size msg_controllen that contains additional information about the packet. To read this field, you first need to declare a struct cmsghdr * (let's call it cmhdr). You populate this by calling CMSG_FIRSTHDR() the first time, passing it the address of the msghdr struct, and CMSG_NXTHDR() each subsequent time, passing it the address of the msghdr struct and the current value of cmhdr.
From the msg_control, you can find interesting things like the destination IP of the packet (useful for multicast) and the contents of the TOS/DSCP byte in the IP header (useful for custom congestion control protocols), among others. In most cases, you'll need to make a setsockopt call to enable receiving this data. In the examples given, the IP_PKTINFO and IP_TOS options need to be enabled.
See the cmsg(3) manpage for more details.
The source IP and port, are not in msg_control, but are in msg_name which expects a pointer to a struct sockaddr with length msg_namelen.
Here's an example of how to use this:
struct msghdr mhdr;
struct iovec iov[1];
struct cmsghdr *cmhdr;
char control[1000];
struct sockaddr_in sin;
char databuf[1500];
unsigned char tos;
mhdr.msg_name = &sin
mhdr.msg_namelen = sizeof(sin);
mhdr.msg_iov = iov;
mhdr.msg_iovlen = 1;
mhdr.msg_control = &control;
mhdr.msg_controllen = sizeof(control);
iov[0].iov_base = databuf;
iov[0].iov_len = sizeof(databuf);
memset(databuf, 0, sizeof(databuf));
if ((*len = recvmsg(sock, &mhdr, 0)) == -1) {
perror("error on recvmsg");
exit(1);
} else {
cmhdr = CMSG_FIRSTHDR(&mhdr);
while (cmhdr) {
if (cmhdr->cmsg_level == IPPROTO_IP && cmhdr->cmsg_type == IP_TOS) {
// read the TOS byte in the IP header
tos = ((unsigned char *)CMSG_DATA(cmhdr))[0];
}
cmhdr = CMSG_NXTHDR(&mhdr, cmhdr);
}
printf("data read: %s, tos byte = %02X\n", databuf, tos);
}

How to find socket port by inode struct in Linux?

I'm writing a LKM program to hook sys_read function for reuse 80 TCP port.
But I meet an problem is that I don't know how to access Port by inode struct.
There is a part of my program.
Linux.2.6.3.38
asmlinkage ssize_t new_read(unsigned int fd, void *buf, size_t count){
//printk("PID %d called sys_read !\n",current->pid);
char kbuf[MAX_BUF];
ssize_t ret;
struct file *file;
ret=orig_read(fd, buf, count);
memset(kbuf, 0,MAX_BUF);
memcpy(kbuf, buf, ret);
printk("kbuf:%s\n",kbuf);
if( memcmp(kbuf, passwd, strlen(passwd)) == 0 )
{
file = fget(fd);
if(file->f_dentry->d_inode->???? == PORT)
printk("get http message\n");
fput(file);
}
}
Thanks for answering.
You can obtain the socket structure pointer from the struct file with the exported function sock_from_file.
It is then cast into a tcp_sock, which contains an inet_connection_sock which contains an inet_sock which contains a sock (not to be confused with socket) which contains sock_common. The two port numbers are ultimately stored in inet_sock and sock_common (well, that's how it works in a recent kernel version anyway).
Making use of these facts in a reliable way would be difficult. The layout and organization of all this is closely dependent on kernel version, and of course that the file descriptor actually represents a connected TCP socket.

How can I get rid of this c warning?

Here sock_client is an socket:
LaunchThread(proxy_handlereq, sock_client);
static void LaunchThread(int (*func)(), void *parg)
{
#ifdef WINDOWS
LPDWORD tid;
CreateThread(NULL, 0L, (void *)func, parg, 0L, &tid);
#else
pthread_t pth;
pthread_create(&pth, NULL, func, parg);
#endif
}
I'm getting the following warning: warning: cast to pointer from integer of different size
How can I pass it as the 2nd parameter of LaunchThread?
Try this:
LaunchThread(proxy_handlereq, (void*)sock_client);
Edit:
Ok, now I see: sock_client is just the integer number of the port.
And you want to pass this number to the other thread, right?
(Depending on the pointer size on your system) you can get rid of the
warning by this dirty cast:
LaunchThread(proxy_handlereq, (void*)(0xFFFFFFFFFFFFFFFF & sock_client);
But actually I would recommend, that you create a data structure with
all the information, that you want to pass to the other thread, e.g.:
struct MyData {
int socket_no;
const char* host_name;
...
};
Then create an instance of this and pass a pointer to the instance to
your LaunchThread function.
Edit2:
You can see some sample code in this question:
Multiple arguments to function called by pthread_create()?
If sock_client is a socket, you need to invoke LaunchThread as:
LaunchThread(proxy_handlereq, &sock_client);
because both CreateThread and pthread_create expect a pointer to the argument to pass on to func().

Resources