File: conn.go

package info (click to toggle)
golang-github-katalix-go-l2tp 0.1.8-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 632 kB
  • sloc: ansic: 127; sh: 10; makefile: 4
file content (127 lines) | stat: -rw-r--r-- 3,216 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
package pppoe

import (
	"fmt"
	"net"
	"os"

	"golang.org/x/sys/unix"
)

// PPPoEConn represents a PPPoE discovery connection, allowing
// receipt and transmission of PPPoE discovery packets.
//
// Because raw sockets are used for sending Ethernet frames, it is
// necessary to have root permissions to create PPPoEConn instances.
type PPPoEConn struct {
	iface *net.Interface
	fd    int
	file  *os.File
}

func newRawSocket(protocol int) (fd int, err error) {

	// raw socket since we want to read/write link-level packets
	fd, err = unix.Socket(unix.AF_PACKET, unix.SOCK_RAW, protocol)
	if err != nil {
		return -1, fmt.Errorf("socket: %v", err)
	}

	// make the socket nonblocking so we can use it with the runtime poller
	if err = unix.SetNonblock(fd, true); err != nil {
		unix.Close(fd)
		return -1, fmt.Errorf("failed to set socket nonblocking: %v", err)
	}

	// set the socket CLOEXEC to prevent passing it to child processes
	flags, err := unix.FcntlInt(uintptr(fd), unix.F_GETFD, 0)
	if err != nil {
		unix.Close(fd)
		return -1, fmt.Errorf("fcntl(F_GETFD): %v", err)
	}

	_, err = unix.FcntlInt(uintptr(fd), unix.F_SETFD, flags|unix.FD_CLOEXEC)
	if err != nil {
		unix.Close(fd)
		return -1, fmt.Errorf("fcntl(F_SETFD, FD_CLOEXEC): %v", err)
	}

	// allow broadcast
	err = unix.SetsockoptInt(fd, unix.SOL_SOCKET, unix.SO_BROADCAST, 1)
	if err != nil {
		unix.Close(fd)
		return -1, fmt.Errorf("setsockopt(SO_BROADCAST): %v", err)
	}

	return
}

// NewDiscoveryConnection creates a new PPPoE discovery connection on
// the specified network interface.
func NewDiscoveryConnection(ifname string) (conn *PPPoEConn, err error) {

	iface, err := net.InterfaceByName(ifname)
	if err != nil {
		return nil, fmt.Errorf("failed to obtain details of interface \"%s\": %v", ifname, err)
	}

	fd, err := newRawSocket(int(ethTypeDiscoveryNetUint16()))
	if err != nil {
		return nil, fmt.Errorf("failed to create raw socket: %v", err)
	}

	// bind to the interface specified
	sa := unix.SockaddrLinklayer{
		Protocol: ethTypeDiscoveryNetUint16(),
		Ifindex:  iface.Index,
	}
	err = unix.Bind(fd, &sa)
	if err != nil {
		unix.Close(fd)
		return nil, fmt.Errorf("failed to bind socket: %v", err)
	}

	// register the socket with the runtime
	file := os.NewFile(uintptr(fd), "pppoe")

	return &PPPoEConn{
		iface: iface,
		fd:    fd,
		file:  file,
	}, nil
}

// Close closes the discovery connection, releasing allocated resources.
func (c *PPPoEConn) Close() (err error) {
	if c.file != nil {
		err = c.file.Close()
		c.file = nil
	}
	return
}

// Send sends a frame over the discovery connection.
func (c *PPPoEConn) Send(b []byte) (n int, err error) {
	return c.file.Write(b)
}

// Recv receives one or more frames over the discovery connection.
func (c *PPPoEConn) Recv(b []byte) (n int, err error) {
	return c.file.Read(b)
}

// HWAddr returns the hardware address of the interface the discovery
// connection is using.
func (c *PPPoEConn) HWAddr() (addr [6]byte) {
	if len(c.iface.HardwareAddr) >= 6 {
		return [6]byte{
			c.iface.HardwareAddr[0],
			c.iface.HardwareAddr[1],
			c.iface.HardwareAddr[2],
			c.iface.HardwareAddr[3],
			c.iface.HardwareAddr[4],
			c.iface.HardwareAddr[5],
		}
	}
	return [6]byte{}
}