给数据包设定mark,最常用的场景就是策略路由了,ip rule根据mark来决定数据包的路由方式,以下是经过验证的,go语言给tcp和udp数据包设定mark的方式。

TCP

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
dialer := &net.Dialer{
Control: func(_, _ string, c syscall.RawConn) error {
	return c.Control(func(fd uintptr) {
		ex := setSocketMark(int(fd), xc.Mark.Value)
		if ex != nil {
			logf("net dialer set mark error: %s", ex)
		}
	})
},
}
rc, e = dialer.Dial("tcp", target)

TCP可以在net.Dialer的Control这个方法内处理。

UDP

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
func setConnMark(conn interface{}, value int) (e error) {
	var f *os.File
	switch conn.(type) {
	case *net.TCPConn:
		f, e = conn.(*net.TCPConn).File()
	case *net.UDPConn:
		f, e = conn.(*net.UDPConn).File()
	default:
		return fmt.Errorf("not support conn(%v), with type(%T)", conn, conn)
	}
	if e != nil {
		return
	}
	defer f.Close()
	return setSocketMark(int(f.Fd()), value)
}

func setSocketMark(fd int, value int) (e error) {
	return syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_MARK, value)
}

UDP通过调用上方提供的setConnMark()函数来处理。