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
74 changes: 57 additions & 17 deletions internal/app/deviceadmin/routes/internet/conn-profiles.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package internet
import (
"context"
"fmt"
"math"
"net/http"
"net/url"
"strconv"
Expand Down Expand Up @@ -235,6 +236,25 @@ func updateConnProfile(
return nmc.UpdateConnProfileByUUID(ctx, uid, updateType, updateValues)
}

func parseConnProfileSettingsField(
key nm.ConnProfileSettingsKey, rawValues []string,
) (update any, err error) {
switch key.Section {
default:
return nil, errors.Errorf("unimplemented settings section %s", key.Section)
case "connection":
return parseConnProfileSettingsConnField(key, rawValues)
case "802-11-wireless":
return parseConnProfileSettingsWifiField(key, rawValues)
case "802-11-wireless-security":
return parseConnProfileSettingsWifiSecField(
key, rawValues,
)
case "ipv4":
return parseConnProfileSettingsIPv4Field(key, rawValues)
}
}

func parseConnProfileSettingsConnField(
key nm.ConnProfileSettingsKey, rawValues []string,
) (parsedValue any, err error) {
Expand Down Expand Up @@ -273,23 +293,6 @@ func parseCheckbox(rawValue, checkedValue, uncheckedValue string) (parsedValue b
}
}

func parseConnProfileSettingsField(
key nm.ConnProfileSettingsKey, rawValues []string,
) (update any, err error) {
switch key.Section {
default:
return nil, errors.Errorf("unimplemented settings section %s", key.Section)
case "connection":
return parseConnProfileSettingsConnField(key, rawValues)
case "802-11-wireless":
return parseConnProfileSettingsWifiField(key, rawValues)
case "802-11-wireless-security":
return parseConnProfileSettingsWifiSecField(
key, rawValues,
)
}
}

func parseConnProfileSettingsWifiField(
key nm.ConnProfileSettingsKey, rawValues []string,
) (parsedValue any, err error) {
Expand Down Expand Up @@ -393,6 +396,43 @@ func parseConnProfileSettingsWifiSecField(
}
}

func parseConnProfileSettingsIPv4Field(
key nm.ConnProfileSettingsKey, rawValues []string,
) (parsedValue any, err error) {
rawValue := rawValues[len(rawValues)-1] // selects the last value to account for single checkboxes
switch key.Key {
default:
return nil, errors.Errorf("unimplemented or unknown key %s", key)
case "dhcp-timeout":
value, err := strconv.Atoi(rawValue)
if err != nil {
return nil, errors.Wrapf(err, "couldn't parse %s as integer", rawValue)
}
if value < 0 || value > math.MaxInt32 {
return nil, errors.Errorf("DHCP timeout %d out of range [0, %d]", value, math.MaxInt32)
}
return value, nil
case "link-local":
mode := nm.NewConnProfileSettingsIPv4LinkLocal(rawValue)
if info := mode.Info(); info.Level == nm.EnumInfoLevelError {
return nil, errors.New(info.Details)
}
return mode, nil
case "may-fail":
mayFail, err := parseCheckbox(rawValue, "optional", "required")
if err != nil {
return false, errors.Wrapf(err, "couldn't parse value for %s", key)
}
return mayFail, nil
case "method":
method := nm.ConnProfileSettingsIPv4Method(rawValue)
if info := method.Info(); info.Level == nm.EnumInfoLevelError {
return nil, errors.New(info.Details)
}
return method, nil
}
}

func checkConnProfile(formValues url.Values) error {
if rawWifiChannel, ok := formValues["802-11-wireless.channel"]; ok {
wifiChannel := rawWifiChannel[0]
Expand Down
109 changes: 105 additions & 4 deletions internal/clients/networkmanager/conn-profiles-settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package networkmanager

import (
"context"
"fmt"
"math"
"strings"
"time"
Expand Down Expand Up @@ -57,15 +58,94 @@ var connProfileSettingsConnTypeInfo = map[ConnProfileSettingsConnType]EnumInfo{
func (t ConnProfileSettingsConnType) Info() EnumInfo {
info, ok := connProfileSettingsConnTypeInfo[t]
if !ok {
return EnumInfo{Short: string(t)}
return EnumInfo{
Short: "unknown",
Details: fmt.Sprintf("unknown type (%s)", t),
Level: EnumInfoLevelError,
}
}
return info
}

type ConnProfileSettingsIPv4 struct {
Addresses []IPAddress
Method string // TODO: change this to a string enum // TODO
NeverDefault bool // TODO
Addresses []IPAddress
DHCPTimeout time.Duration
LinkLocal ConnProfileSettingsIPv4LinkLocal
MayFail bool
Method ConnProfileSettingsIPv4Method
}

type ConnProfileSettingsIPv4LinkLocal int32

var connProfileSettingsIPv4LinkLocalInfo = map[ConnProfileSettingsIPv4LinkLocal]EnumInfo{
0: {
Short: "default",
},
1: {
Short: "auto",
},
2: {
Short: "disabled",
},
3: {
Short: "enabled",
},
4: {
Short: "fallback",
},
}

func NewConnProfileSettingsIPv4LinkLocal(infoShort string) ConnProfileSettingsIPv4LinkLocal {
for key, value := range connProfileSettingsIPv4LinkLocalInfo {
if value.Short == infoShort {
return key
}
}
return -1
}

func (l ConnProfileSettingsIPv4LinkLocal) Info() EnumInfo {
info, ok := connProfileSettingsIPv4LinkLocalInfo[l]
if !ok {
return EnumInfo{
Short: "unknown",
Details: fmt.Sprintf("unknown link-local behavior (%d)", l),
Level: EnumInfoLevelError,
}
}
return info
}

type ConnProfileSettingsIPv4Method string

var connProfileSettingsIPv4MethodInfo = map[ConnProfileSettingsIPv4Method]EnumInfo{
"disabled": {
Short: "disabled",
},
"auto": {
Short: "auto",
},
"manual": {
Short: "manual",
},
"link-local": {
Short: "link-local",
},
"shared": {
Short: "shared",
},
}

func (m ConnProfileSettingsIPv4Method) Info() EnumInfo {
info, ok := connProfileSettingsIPv4MethodInfo[m]
if !ok {
return EnumInfo{
Short: "unknown",
Details: fmt.Sprintf("unknown setting (%s)", m),
Level: EnumInfoLevelError,
}
}
return info
}

type ConnProfileSettingsIPv6 struct {
Expand Down Expand Up @@ -242,6 +322,27 @@ func dumpConnProfileSettingsIPv4(
s.Addresses = append(s.Addresses, address)
}

if s.MayFail, err = ensureVar(rawSettings, "may-fail", "", false, true); err != nil {
return s, err
}

rawDHCPTimeout, err := ensureVar(rawSettings, "dhcp-timeout", "", false, 0)
if err != nil {
return s, err
}
s.DHCPTimeout = time.Duration(rawDHCPTimeout) * time.Second

rawLinkLocal, err := ensureVar(rawSettings, "link-local", "", false, 0)
if err != nil {
return s, err
}
s.LinkLocal = ConnProfileSettingsIPv4LinkLocal(rawLinkLocal)
rawMethod, err := ensureVar(rawSettings, "method", "", true, "")
if err != nil {
return s, err
}
s.Method = ConnProfileSettingsIPv4Method(rawMethod)

return s, nil
}

Expand Down
145 changes: 145 additions & 0 deletions web/templates/internet/conn-profiles/settings-form.partial.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -679,6 +679,151 @@

<h3>IPv4</h3>
{{$ipv4 := $settings.IPv4}}
<div class="field is-horizontal">
<div class="field-label is-normal">
<label class="label">
<abbr title="IPv4 configuration method">
Method
</abbr>
</label>
</div>
<div class="field-body">
<div class="field">
<div class="control">
<div class="select">
<select name="ipv4.method">
<option
value="disabled"
{{if eq $ipv4.Method.Info.Short "disabled"}}selected{{end}}
>
disabled (no IPv4)
</option>
<option
value="auto"
{{if eq $ipv4.Method.Info.Short "auto"}}selected{{end}}
>
auto (with from DHCP, PPP, or similar services)
</option>
<option
value="manual"
{{if eq $ipv4.Method.Info.Short "manual"}}selected{{end}}
>
manual (static) IP address
</option>
<option
value="link-local"
{{if eq $ipv4.Method.Info.Short "link-local"}}selected{{end}}
>
only link-local (random) IP address
</option>
<option
value="shared"
{{if eq $ipv4.Method.Info.Short "shared"}}selected{{end}}
>
shared (share internet access to connected devices)
</option>
</select>
</div>
</div>
</div>
</div>
</div>

<div class="field is-horizontal">
<div class="field-label is-normal">
<label class="label">
<abbr title="whether to obtain IPv4 link-local address, overriding the specified IPv4 method">
Link-local?
</abbr>
</label>
</div>
<div class="field-body">
<div class="field">
<div class="control">
<div class="select">
<select name="ipv4.link-local">
<option
value="default"
{{if eq $ipv4.LinkLocal.Info.Short "default"}}selected{{end}}
>
default (use global default or fall back to auto)
</option>
<option
value="auto"
{{if eq $ipv4.LinkLocal.Info.Short "auto"}}selected{{end}}
>
auto (don't override specified IPv4 method)
</option>
<option
value="disabled"
{{if eq $ipv4.LinkLocal.Info.Short "disabled"}}selected{{end}}
>
disabled (never obtain)
</option>
<option
value="enabled"
{{if eq $ipv4.LinkLocal.Info.Short "enabled"}}selected{{end}}
>
enabled (always obtain)
</option>
<option
value="fallback"
{{if eq $ipv4.LinkLocal.Info.Short "fallback"}}selected{{end}}
>
fallback (only obtain if no other IPv4 address is set)
</option>
</select>
</div>
</div>
</div>
</div>
</div>

<div class="field is-horizontal">
<div class="field-label is-normal">
<label class="label">
<abbr title="how long to wait before giving up on DHCP requests; 0 specifies to use the global default, while 2,147,483,647 specifies to never give up">
Timeout
</abbr>
</label>
</div>
<div class="field-body">
<div class="field">
<div class="control">
<input
class="input" type="number"
name="ipv4.dhcp-timeout"
min="0" max="2147483647"
value="{{round $ipv4.DHCPTimeout.Seconds 0}}"
>
</div>
</div>
</div>
</div>

<div class="field is-horizontal">
<div class="field-label">
<label class="label">
<abbr title="whether IPv4 configuration must succeed for overall network configuration to be considered successful">
Required?
</abbr>
</label>
</div>
<div class="field-body">
<div class="field">
<div class="control">
<input type="hidden" name="ipv4.may-fail" value="optional">
<input type="checkbox"
name="ipv4.may-fail"
value="required"
autocomplete="off"
{{if not $ipv4.MayFail}}checked{{end}}
/>
</div>
</div>
</div>
</div>

<div class="field is-horizontal">
<div class="field-label">
<label class="label">Addresses</label>
Expand Down
Loading