Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 104 additions & 0 deletions Documentation/FLAGS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# BP Socket Flags Documentation

This document describes the flags available for use with BP (Bundle Protocol) sockets in both `sendmsg()` and `recvmsg()` operations.

## Receive Message Flags

Applications can use the following flags with `recvmsg()`:

| Flag | Description |
| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `MSG_PEEK` | Peeks at the incoming datagram without removing it from the queue. Useful for inspecting message size or content before full reception. |
| `MSG_TRUNC` | Returns the full size of the message, even if the provided buffer is too small. The datagram is consumed from the queue, and only the portion that fits is copied to the buffer. The MSG_TRUNC flag is set in msg_flags. |

### Combined Usage

The most efficient way to determine message size without consuming it:

```c
ssize_t need = recvmsg(fd, &msg, MSG_PEEK | MSG_TRUNC);
```

This combination allows you to:

1. Get the exact message size
2. Keep the message in the queue for later consumption
3. Allocate the proper buffer size dynamically

## Send Message Flags

The following flags can be used with `sendmsg()` to tune message delivery, reporting, priority, and custody:

### Acknowledgment

| Flag | Description |
| ------------------- | ------------------------------------------ |
| `MSG_ACK_REQUESTED` | Requests an acknowledgment for the message |

### Status Reports (combinable)

These flags can be combined using the bitwise OR operator (`|`):

| Flag | Description |
| ------------------- | ---------------------------------------------------- |
| `MSG_RECEIVED_RPT` | Request a report when the message is received |
| `MSG_CUSTODY_RPT` | Request a report when custody of the message changes |
| `MSG_FORWARDED_RPT` | Request a report when the message is forwarded |
| `MSG_DELIVERED_RPT` | Request a report when the message is delivered |
| `MSG_DELETED_RPT` | Request a report when the message is deleted |

### Priority (mutually exclusive)

Only one of these priority flags can be used at a time:

| Flag | Description |
| --------------------------- | -------------------------------------------- |
| `MSG_BP_BULK_PRIORITY` | Assigns a bulk priority to the message |
| `MSG_BP_STD_PRIORITY` | Assigns a standard priority to the message |
| `MSG_BP_EXPEDITED_PRIORITY` | Assigns an expedited priority to the message |

### Custody (mutually exclusive)

Only one of these custody flags can be used at a time:

| Flag | Description |
| ----------------------------- | ---------------------------------------------------- |
| `MSG_SOURCE_CUSTODY_REQUIRED` | Requires the source to retain custody of the message |
| `MSG_SOURCE_CUSTODY_OPTIONAL` | Source custody is optional |
| `MSG_NO_CUSTODY_REQUIRED` | No custody is required |

## Usage Examples

### Receiving Messages with Dynamic Buffer Allocation

```c
// Step 1: Get message size without consuming
ssize_t message_size = recvmsg(fd, &msg, MSG_PEEK | MSG_TRUNC);

// Step 2: Allocate proper buffer and receive
char *buffer = malloc(message_size + 1);
struct iovec iov = { .iov_base = buffer, .iov_len = message_size };
msg.msg_iov = &iov;
msg.msg_iovlen = 1;

ssize_t received = recvmsg(fd, &msg, 0);
buffer[received] = '\0';
```

### Sending Messages with Flags

```c
// Send with acknowledgment request and no custody requirement
ssize_t sent = sendmsg(fd, &msg, MSG_ACK_REQUESTED | MSG_NO_CUSTODY_REQUIRED);

// Send with status reports and expedited priority
ssize_t sent = sendmsg(fd, &msg,
MSG_RECEIVED_RPT | MSG_DELIVERED_RPT | MSG_BP_EXPEDITED_PRIORITY);
```

## Implementation Notes

- All BP-specific flags are defined in `include/bp_socket.h`
- Standard socket flags (MSG_PEEK, MSG_TRUNC) are available through system headers
- Flag values are designed to avoid conflicts with standard socket flags
- The kernel implementation properly handles all flag combinations
57 changes: 57 additions & 0 deletions Documentation/OPTIONS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# BP Socket Options Documentation

This document describes the socket options available for BP (Bundle Protocol) sockets using `setsockopt()` and `getsockopt()`.

## Overview

BP sockets support standard socket options at the `SOL_SOCKET` level, with specific implementations for timeout handling. All other socket levels are passed through to the common socket implementation.

## Supported Socket Options

### Receive Timeout Options

BP sockets support the standard POSIX receive timeout option:

| Option Name | Type | Description |
| ------------- | ---------------- | ------------------------------ |
| `SO_RCVTIMEO` | `struct timeval` | Standard POSIX receive timeout |

### Timeout Behavior

- **Default**: No timeout (`MAX_SCHEDULE_TIMEOUT`)
- **Zero timeout**: Immediate timeout (non-blocking behavior)
- **Non-zero timeout**: Block for specified duration before timing out
- **Timeout units**: seconds and microseconds (standard POSIX)

## Usage Examples

### Setting Receive Timeout

```c
#include <sys/socket.h>
#include <sys/time.h>

struct timeval timeout;
timeout.tv_sec = 5; // 5 seconds
timeout.tv_usec = 0; // 0 microseconds

if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) < 0) {
perror("setsockopt failed");
return -1;
}
```

### Getting Current Timeout

```c
struct timeval timeout;
socklen_t len = sizeof(timeout);

if (getsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, &len) < 0) {
perror("getsockopt failed");
return -1;
}

printf("Current timeout: %ld.%06ld seconds\n",
timeout.tv_sec, timeout.tv_usec);
```
37 changes: 25 additions & 12 deletions bp_socket/af_bp.c
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,7 @@ int bp_recvmsg(struct socket* sock, struct msghdr* msg, size_t size, int flags)
struct sk_buff* skb = NULL;
struct sockaddr_bp* src_addr;
long timeo;
size_t copy_len;
int ret;

sk = sock->sk;
Expand Down Expand Up @@ -358,7 +359,11 @@ int bp_recvmsg(struct socket* sock, struct msghdr* msg, size_t size, int flags)
}

mutex_lock(&bp->rx_mutex);
skb = skb_dequeue(&bp->rx_queue);
if (flags & MSG_PEEK) {
skb = skb_peek(&bp->rx_queue);
} else {
skb = skb_dequeue(&bp->rx_queue);
}
if (!skb) {
pr_info("bp_recvmsg: no messages in the queue for ipn:%u.%u\n",
bp->bp_node_id, bp->bp_service_id);
Expand All @@ -369,11 +374,16 @@ int bp_recvmsg(struct socket* sock, struct msghdr* msg, size_t size, int flags)
mutex_unlock(&bp->rx_mutex);

if (skb->len > size) {
pr_err("bp_recvmsg: buffer too small for message (required=%u, "
"provided=%zu)\n",
skb->len, size);
ret = -EMSGSIZE;
goto out;
if (flags & MSG_TRUNC) {
msg->msg_flags |= MSG_TRUNC;
} else {
pr_err("bp_recvmsg: buffer too small for message "
"(required=%u, "
"provided=%zu)\n",
skb->len, size);
ret = -EMSGSIZE;
goto out;
}
}

if (msg->msg_name) {
Expand All @@ -386,22 +396,25 @@ int bp_recvmsg(struct socket* sock, struct msghdr* msg, size_t size, int flags)
msg->msg_namelen = sizeof(struct sockaddr_bp);
}

if (copy_to_iter(skb->data, skb->len, &msg->msg_iter) != skb->len) {
copy_len = (skb->len > size) ? size : skb->len;
if (copy_to_iter(skb->data, copy_len, &msg->msg_iter) != copy_len) {
pr_err("bp_recvmsg: failed to copy data to user space\n");
ret = -EFAULT;
goto out;
}

ret = destroy_bundle_doit(BP_SKB_CB(skb)->adu, 8443);
if (ret < 0) {
pr_warn(
"bp_recvmsg: failed to destroy bundle, bundle may leak\n");
if (!(flags & MSG_PEEK)) {
ret = destroy_bundle_doit(BP_SKB_CB(skb)->adu, 8443);
if (ret < 0) {
pr_warn("bp_recvmsg: failed to destroy bundle, bundle "
"may leak\n");
}
}

ret = skb->len;

out:
if (skb)
if (skb && !(flags & MSG_PEEK))
kfree_skb(skb);
release_sock(sk);
return ret;
Expand Down