網絡子係統81_inet協議族-SOCK_RAW(二)
// struct sock->sk_prot字段
// struct proto為插口層到傳輸層的接口
4.1 struct proto raw_prot = {
.name = "RAW",
.owner = THIS_MODULE,
.close = raw_close,
.destroy = raw_destroy,
.connect = ip4_datagram_connect,
.disconnect = udp_disconnect,
.ioctl = raw_ioctl,
.init = raw_init,
.setsockopt = raw_setsockopt,
.getsockopt = raw_getsockopt,
.sendmsg = raw_sendmsg,
.recvmsg = raw_recvmsg,
.bind = raw_bind,
.backlog_rcv = raw_rcv_skb,
.release_cb = ip4_datagram_release_cb,
.hash = raw_hash_sk,
.unhash = raw_unhash_sk,
.obj_size = sizeof(struct raw_sock),
.h.raw_hash = &raw_v4_hashinfo,
};
// raw sock綁定地址到socket
4.1 static int raw_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
{
struct inet_sock *inet = inet_sk(sk);
struct sockaddr_in *addr = (struct sockaddr_in *) uaddr;
int ret = -EINVAL;
int chk_addr_ret;
//sk->sk_state!=TCP_CLOSE說明連接已經建立
if (sk->sk_state != TCP_CLOSE || addr_len < sizeof(struct sockaddr_in))
goto out;
//檢查綁定的地址類型是否合法
chk_addr_ret = inet_addr_type(sock_net(sk), addr->sin_addr.s_addr);
ret = -EADDRNOTAVAIL;
//合法的綁定地址類型:本地地址,多播地址,廣播地址
if (addr->sin_addr.s_addr && chk_addr_ret != RTN_LOCAL &&
chk_addr_ret != RTN_MULTICAST && chk_addr_ret != RTN_BROADCAST)
goto out;
//多播,廣播地址,源地址使用接口設備的地址
inet->inet_rcv_saddr = inet->inet_saddr = addr->sin_addr.s_addr;
if (chk_addr_ret == RTN_MULTICAST || chk_addr_ret == RTN_BROADCAST)
inet->inet_saddr = 0;
sk_dst_reset(sk);
ret = 0;
out: return ret;
}
// 建立連接
// 步驟:
// 1.建立路由信息
// 2.更新sock->sk_state=TCP_ESTABLISHED表示建立了連接
4.2 int ip4_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
{
struct inet_sock *inet = inet_sk(sk);
struct sockaddr_in *usin = (struct sockaddr_in *) uaddr;
struct flowi4 *fl4;
struct rtable *rt;
__be32 saddr;
int oif;
int err;
//重置inet的路由信息
sk_dst_reset(sk);
lock_sock(sk);
//sock綁定的設備(可通過setsockopt建立)
oif = sk->sk_bound_dev_if;
saddr = inet->inet_saddr;
fl4 = &inet->cork.fl.u.ip4;
//建立路由信息
rt = ip_route_connect(fl4, usin->sin_addr.s_addr, saddr,
RT_CONN_FLAGS(sk), oif,
sk->sk_protocol,
inet->inet_sport, usin->sin_port, sk, true);
...
inet->inet_daddr = fl4->daddr;
inet->inet_dport = usin->sin_port;
//更新sk->sk_state表示建立了連接
sk->sk_state = TCP_ESTABLISHED;
inet->inet_id = jiffies;
//設置sock路由緩存
sk_dst_set(sk, &rt->dst);
err = 0;
out:
release_sock(sk);
return err;
}
// 發送數據
// 步驟:
// 1.獲取目的地址
// 1.1 通過msghdr或inet_sock
// 2.初始化flow4結構體
// 3.通過flow4查找路由表,獲取路由信息
// 4.將數據報添加到sk->sk_write_queue
// 5.沒有設置MSG_MORE,通知ip層進程數據傳輸
4.3 static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
size_t len)
{
struct inet_sock *inet = inet_sk(sk);
struct ipcm_cookie ipc;
struct rtable *rt = NULL;
struct flowi4 fl4;
int free = 0;
__be32 daddr;
__be32 saddr;
u8 tos;
int err;
struct ip_options_data opt_copy;
...
ipc.addr = inet->inet_saddr;
ipc.opt = NULL;
ipc.tx_flags = 0;
//使用bound的出口設備
ipc.oif = sk->sk_bound_dev_if;
saddr = ipc.addr;
ipc.addr = daddr;
tos = RT_CONN_FLAGS(sk);
//初始化flowi4結構
flowi4_init_output(&fl4, ipc.oif, sk->sk_mark, tos,
RT_SCOPE_UNIVERSE,
inet->hdrincl ? IPPROTO_RAW : sk->sk_protocol,
inet_sk_flowi_flags(sk) | FLOWI_FLAG_CAN_SLEEP,
daddr, saddr, 0, 0);
//查找路由表,獲得路由信息
rt = ip_route_output_flow(sock_net(sk), &fl4, sk);
err = -EACCES;
//路由結果為廣播,但是套接字不允許發送廣播報文,直接返回
if (rt->rt_flags & RTCF_BROADCAST && !sock_flag(sk, SOCK_BROADCAST))
goto done;
//目的地址
if (!ipc.addr)
ipc.addr = fl4.daddr;
lock_sock(sk);
//添加數據報到sock->sk_write_queue
err = ip_append_data(sk, &fl4, ip_generic_getfrag, msg->msg_iov, len, 0, &ipc, &rt, msg->msg_flags);
//出現錯誤,丟棄所有已經pending的ip報文
if (err)
ip_flush_pending_frames(sk);
//MSG_MORE表示接下來還有msg
else if (!(msg->msg_flags & MSG_MORE)) {
//將sk_write_queue上的數據發送出去
err = ip_push_pending_frames(sk, &fl4);
if (err == -ENOBUFS && !inet->recverr)
err = 0;
release_sock(sk);
}
done:
if (free)
kfree(ipc.opt);
//遞減路由信息的引用計數
ip_rt_put(rt);
out:
if (err < 0)
return err;
return len;
}
// 接收數據
// 步驟:
// 1.接收數據報
// 2.將數據報拷貝到iovec中
4.4 static int raw_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
size_t len, int noblock, int flags, int *addr_len)
{
struct inet_sock *inet = inet_sk(sk);
size_t copied = 0;
int err = -EOPNOTSUPP;
struct sockaddr_in *sin = (struct sockaddr_in *)msg->msg_name;
struct sk_buff *skb;
//接收數據報
skb = skb_recv_datagram(sk, flags, noblock, &err);
if (!skb)
goto out;
//將數據從skb拷貝到iovec
err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
if (err)
goto done;
//拷貝skb中的源地址
if (sin) {
sin->sin_family = AF_INET;
sin->sin_addr.s_addr = ip_hdr(skb)->saddr;
sin->sin_port = 0;
memset(&sin->sin_zero, 0, sizeof(sin->sin_zero));
}
done:
skb_free_datagram(sk, skb);
out:
if (err)
return err;
return copied;
}
// 接收數據報
// 步驟:
// 1.獲取允許的阻塞時間
// 2.如果sock->sk_receive_queue有接收到的skb,返回skb
// 3.如果可阻塞等待,則阻塞,否則直接返回null
4.5 struct sk_buff *__skb_recv_datagram(struct sock *sk, unsigned int flags,
int *peeked, int *off, int *err)
{
struct sk_buff *skb;
long timeo;
//計算等待到期時間
timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT);
do {
//取下sock->sk_receive_queue接收到的skb
unsigned long cpu_flags;
struct sk_buff_head *queue = &sk->sk_receive_queue;
spin_lock_irqsave(&queue->lock, cpu_flags);
skb_queue_walk(queue, skb) {
__skb_unlink(skb, queue);
spin_unlock_irqrestore(&queue->lock, cpu_flags);
return skb;
}
spin_unlock_irqrestore(&queue->lock, cpu_flags);
error = -EAGAIN;
if (!timeo)
goto no_packet;
//等待數據到來
} while (!wait_for_packet(sk, err, &timeo));
return NULL;
no_packet:
*err = error;
return NULL;
}
// 在sock上等待數據到來
// 1. 創建wait塊,指定wait_func
// 2. 將wait塊添加到sock->sk_wq上
// 3. 通過schedule_timeout阻塞
// 注:
// 創建的wait塊添加到sock->sk_wq用於在有事件發生時,喚醒阻塞線程,
// 但是阻塞是通過schedule_timeout執行,而非wait塊
4.6 static int wait_for_packet(struct sock *sk, int *err, long *timeo_p)
{
int error;
//創建wait塊
DEFINE_WAIT_FUNC(wait, receiver_wake_function);
//將wait塊添加到sock->sk_wq上
prepare_to_wait_exclusive(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE);
...
error = 0;
//重調度
*timeo_p = schedule_timeout(*timeo_p);
out:
finish_wait(sk_sleep(sk), &wait);
return error;
}
最後更新:2017-04-03 12:55:18
上一篇:
2012藍橋杯【初賽試題】幹支紀年
下一篇:
(function(){}})(); and (function(){}());的區別?
cf 154.div2 D. Table with Letters - 2
阿裏2018校招啟動—找對味,搏出way!
設計模式之裝飾模式
???????????????Elasticsearch????????????2????????????2.4.2???????????????????????????-??????-????????????-?????????
Spring-HelloWorld搭建
阿裏集團明確五新戰略三駕馬車:基礎設施落地 戰略思維輸出 投資拉動
Velocity官方指南-使用Velocity
互聯網時代常用網站資源整理匯總(一直在完善中)
jdk安裝後為什麼要配置Path環境變量
雲上安全三字經