1package netlink
2
3import (
4	"fmt"
5	"net"
6	"strings"
7	"syscall"
8
9	"github.com/vishvananda/netlink/nl"
10	"golang.org/x/sys/unix"
11)
12
13type PDP struct {
14	Version     uint32
15	TID         uint64
16	PeerAddress net.IP
17	MSAddress   net.IP
18	Flow        uint16
19	NetNSFD     uint32
20	ITEI        uint32
21	OTEI        uint32
22}
23
24func (pdp *PDP) String() string {
25	elems := []string{}
26	elems = append(elems, fmt.Sprintf("Version: %d", pdp.Version))
27	if pdp.Version == 0 {
28		elems = append(elems, fmt.Sprintf("TID: %d", pdp.TID))
29	} else if pdp.Version == 1 {
30		elems = append(elems, fmt.Sprintf("TEI: %d/%d", pdp.ITEI, pdp.OTEI))
31	}
32	elems = append(elems, fmt.Sprintf("MS-Address: %s", pdp.MSAddress))
33	elems = append(elems, fmt.Sprintf("Peer-Address: %s", pdp.PeerAddress))
34	return fmt.Sprintf("{%s}", strings.Join(elems, " "))
35}
36
37func (p *PDP) parseAttributes(attrs []syscall.NetlinkRouteAttr) error {
38	for _, a := range attrs {
39		switch a.Attr.Type {
40		case nl.GENL_GTP_ATTR_VERSION:
41			p.Version = native.Uint32(a.Value)
42		case nl.GENL_GTP_ATTR_TID:
43			p.TID = native.Uint64(a.Value)
44		case nl.GENL_GTP_ATTR_PEER_ADDRESS:
45			p.PeerAddress = net.IP(a.Value)
46		case nl.GENL_GTP_ATTR_MS_ADDRESS:
47			p.MSAddress = net.IP(a.Value)
48		case nl.GENL_GTP_ATTR_FLOW:
49			p.Flow = native.Uint16(a.Value)
50		case nl.GENL_GTP_ATTR_NET_NS_FD:
51			p.NetNSFD = native.Uint32(a.Value)
52		case nl.GENL_GTP_ATTR_I_TEI:
53			p.ITEI = native.Uint32(a.Value)
54		case nl.GENL_GTP_ATTR_O_TEI:
55			p.OTEI = native.Uint32(a.Value)
56		}
57	}
58	return nil
59}
60
61func parsePDP(msgs [][]byte) ([]*PDP, error) {
62	pdps := make([]*PDP, 0, len(msgs))
63	for _, m := range msgs {
64		attrs, err := nl.ParseRouteAttr(m[nl.SizeofGenlmsg:])
65		if err != nil {
66			return nil, err
67		}
68		pdp := &PDP{}
69		if err := pdp.parseAttributes(attrs); err != nil {
70			return nil, err
71		}
72		pdps = append(pdps, pdp)
73	}
74	return pdps, nil
75}
76
77func (h *Handle) GTPPDPList() ([]*PDP, error) {
78	f, err := h.GenlFamilyGet(nl.GENL_GTP_NAME)
79	if err != nil {
80		return nil, err
81	}
82	msg := &nl.Genlmsg{
83		Command: nl.GENL_GTP_CMD_GETPDP,
84		Version: nl.GENL_GTP_VERSION,
85	}
86	req := h.newNetlinkRequest(int(f.ID), unix.NLM_F_DUMP)
87	req.AddData(msg)
88	msgs, err := req.Execute(unix.NETLINK_GENERIC, 0)
89	if err != nil {
90		return nil, err
91	}
92	return parsePDP(msgs)
93}
94
95func GTPPDPList() ([]*PDP, error) {
96	return pkgHandle.GTPPDPList()
97}
98
99func gtpPDPGet(req *nl.NetlinkRequest) (*PDP, error) {
100	msgs, err := req.Execute(unix.NETLINK_GENERIC, 0)
101	if err != nil {
102		return nil, err
103	}
104	pdps, err := parsePDP(msgs)
105	if err != nil {
106		return nil, err
107	}
108	if len(pdps) != 1 {
109		return nil, fmt.Errorf("invalid reqponse for GENL_GTP_CMD_GETPDP")
110	}
111	return pdps[0], nil
112}
113
114func (h *Handle) GTPPDPByTID(link Link, tid int) (*PDP, error) {
115	f, err := h.GenlFamilyGet(nl.GENL_GTP_NAME)
116	if err != nil {
117		return nil, err
118	}
119	msg := &nl.Genlmsg{
120		Command: nl.GENL_GTP_CMD_GETPDP,
121		Version: nl.GENL_GTP_VERSION,
122	}
123	req := h.newNetlinkRequest(int(f.ID), 0)
124	req.AddData(msg)
125	req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_VERSION, nl.Uint32Attr(0)))
126	req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_LINK, nl.Uint32Attr(uint32(link.Attrs().Index))))
127	req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_TID, nl.Uint64Attr(uint64(tid))))
128	return gtpPDPGet(req)
129}
130
131func GTPPDPByTID(link Link, tid int) (*PDP, error) {
132	return pkgHandle.GTPPDPByTID(link, tid)
133}
134
135func (h *Handle) GTPPDPByITEI(link Link, itei int) (*PDP, error) {
136	f, err := h.GenlFamilyGet(nl.GENL_GTP_NAME)
137	if err != nil {
138		return nil, err
139	}
140	msg := &nl.Genlmsg{
141		Command: nl.GENL_GTP_CMD_GETPDP,
142		Version: nl.GENL_GTP_VERSION,
143	}
144	req := h.newNetlinkRequest(int(f.ID), 0)
145	req.AddData(msg)
146	req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_VERSION, nl.Uint32Attr(1)))
147	req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_LINK, nl.Uint32Attr(uint32(link.Attrs().Index))))
148	req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_I_TEI, nl.Uint32Attr(uint32(itei))))
149	return gtpPDPGet(req)
150}
151
152func GTPPDPByITEI(link Link, itei int) (*PDP, error) {
153	return pkgHandle.GTPPDPByITEI(link, itei)
154}
155
156func (h *Handle) GTPPDPByMSAddress(link Link, addr net.IP) (*PDP, error) {
157	f, err := h.GenlFamilyGet(nl.GENL_GTP_NAME)
158	if err != nil {
159		return nil, err
160	}
161	msg := &nl.Genlmsg{
162		Command: nl.GENL_GTP_CMD_GETPDP,
163		Version: nl.GENL_GTP_VERSION,
164	}
165	req := h.newNetlinkRequest(int(f.ID), 0)
166	req.AddData(msg)
167	req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_VERSION, nl.Uint32Attr(0)))
168	req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_LINK, nl.Uint32Attr(uint32(link.Attrs().Index))))
169	req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_MS_ADDRESS, []byte(addr.To4())))
170	return gtpPDPGet(req)
171}
172
173func GTPPDPByMSAddress(link Link, addr net.IP) (*PDP, error) {
174	return pkgHandle.GTPPDPByMSAddress(link, addr)
175}
176
177func (h *Handle) GTPPDPAdd(link Link, pdp *PDP) error {
178	f, err := h.GenlFamilyGet(nl.GENL_GTP_NAME)
179	if err != nil {
180		return err
181	}
182	msg := &nl.Genlmsg{
183		Command: nl.GENL_GTP_CMD_NEWPDP,
184		Version: nl.GENL_GTP_VERSION,
185	}
186	req := h.newNetlinkRequest(int(f.ID), unix.NLM_F_EXCL|unix.NLM_F_ACK)
187	req.AddData(msg)
188	req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_VERSION, nl.Uint32Attr(pdp.Version)))
189	req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_LINK, nl.Uint32Attr(uint32(link.Attrs().Index))))
190	req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_PEER_ADDRESS, []byte(pdp.PeerAddress.To4())))
191	req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_MS_ADDRESS, []byte(pdp.MSAddress.To4())))
192
193	switch pdp.Version {
194	case 0:
195		req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_TID, nl.Uint64Attr(pdp.TID)))
196		req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_FLOW, nl.Uint16Attr(pdp.Flow)))
197	case 1:
198		req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_I_TEI, nl.Uint32Attr(pdp.ITEI)))
199		req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_O_TEI, nl.Uint32Attr(pdp.OTEI)))
200	default:
201		return fmt.Errorf("unsupported GTP version: %d", pdp.Version)
202	}
203	_, err = req.Execute(unix.NETLINK_GENERIC, 0)
204	return err
205}
206
207func GTPPDPAdd(link Link, pdp *PDP) error {
208	return pkgHandle.GTPPDPAdd(link, pdp)
209}
210
211func (h *Handle) GTPPDPDel(link Link, pdp *PDP) error {
212	f, err := h.GenlFamilyGet(nl.GENL_GTP_NAME)
213	if err != nil {
214		return err
215	}
216	msg := &nl.Genlmsg{
217		Command: nl.GENL_GTP_CMD_DELPDP,
218		Version: nl.GENL_GTP_VERSION,
219	}
220	req := h.newNetlinkRequest(int(f.ID), unix.NLM_F_EXCL|unix.NLM_F_ACK)
221	req.AddData(msg)
222	req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_VERSION, nl.Uint32Attr(pdp.Version)))
223	req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_LINK, nl.Uint32Attr(uint32(link.Attrs().Index))))
224
225	switch pdp.Version {
226	case 0:
227		req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_TID, nl.Uint64Attr(pdp.TID)))
228	case 1:
229		req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_I_TEI, nl.Uint32Attr(pdp.ITEI)))
230	default:
231		return fmt.Errorf("unsupported GTP version: %d", pdp.Version)
232	}
233	_, err = req.Execute(unix.NETLINK_GENERIC, 0)
234	return err
235}
236
237func GTPPDPDel(link Link, pdp *PDP) error {
238	return pkgHandle.GTPPDPDel(link, pdp)
239}
240